As I've covered earlier
, I'm very interested in the unit testing and behavior testing story in F# and functional programming. And as I've indicated earlier, I'm pretty fascinated by Domain Specific Languages (DSLs) as well in this regard. In the past, I've posted about some of the interesting things that are coming from the DSL community, especially from the .NET community itself. I wanted to challenge myself to prove that F# can also produce very readable and powerful DSLs as well. I think with the language flexibility that F# offers, I should be able to get quite expressive
Starting with DSLs
In part of what has me fascinated by DSLs lately is the recent book activity from both Martin Fowler
. Martin has been posting his Domain Specific Languages Work In Progress
and keeping us somewhat up to date on its progress. So far I've been rather impressed with the book and I hope it continues. Although I think fluent builders are nice, I don't think they sometimes have the expressiveness of DSLs that I'm looking for. Many of the samples are written in both Java and C#, and it gives you a pretty good idea where he's going. However, I don't find the intent to be as clear though as DSLs written in Ruby, Boo or F#.
Ayende on the other hand has been working on his books "DSLs in Boo"
that is partially available online right now. It's been interesting to see how flexible the language is in regards to DSLs and seeing it twist in new ways is pretty interesting. The first chaper is available for free, so you can check it out for yourself.
But with this, I was seeing such opportunities when I've been experimenting with language oriented programmiing and F#. Where are there opportunities?
Language Oriented Programming and FsUnit
I recently came across a dormant project on Google Code called FsUnit
, which was a syntactic extension to NUnit
using the F# language semantics. This to me looked like a great example of language oriented programming with F# and some of its uses. There isn't much to this library, yet very powerful. Let's take a look at a few samples:
1 |> should (equal 1)
"two" |> should (notEqual "three")
[| 1 .. 10 |] |> should (contain 5)
What makes this possible and pretty easy to understand? That would be the use of the forward operator. I've covered this operator in the past, but I can't emphasize its importance in this regard. If you're not familiar with that post, let's go over the basics. The basic definition of this operator is that it's just function application in reverse.
The forward operator has a basic definition that looks like this:
let (|>) x f = f x
What this allows us to do is put the first argument last. Let's take a quick example of just a simple forward operator:
let newList = [1..10] |> List.map(fun x -> x + x)
What it essentially becomes is the following code:
let newList = List.map(fun x -> x + x ) [1..10]
This gives us a couple of advantages doing this which includes:
- Intent Clarity - Allows you to perform data transformations, iterations, filtering, etc in a forward chaining fashion
- Type Inference - Since we're chaining together methods, we can better type infer when we specify the data first to the functions that will flow across all of them.
Just as an aside, Chris Smith
, of the F# team, follows up from his recent presentation on Language Oriented Programming with F#
. It's well worth the time invested to look at the possibilities that F# as a language offers. I am also hoping to do some presentations on this in the future as there is a lot of ground to cover with this language.
Replicating with C#?
During some of my recent talks on functional programming and F#, I have come across some resistance to the language itself because many saw that they can replicate a lot of the behaviors in C# 3.0. Indeed that is the case with many things, but not so much with where I'm going. One of my many gripes with C# and in turn the BCL is the fact that they don't treat Void (or Unit to those F#'ers) as a first class citizen. Therefore, I'm stuck with either using a Func<TArg, TResult> delegate or the Action<T> delegate for many operations and never the two shall combine. Whereas in F#, I'm free to create FastFunc<TArg, TResult> delegates with a Unit return type which returns nothing. It's a thing of beauty and I didn't have to mangle my code to get there.
So, with that in mind, let's see if we can replicate the forward operator in C# to the extent that I did in F#. The concept isn't really hard at all when you think about it. We're just calling a function with the arguments reversed. Let's create one with an actual return type, because remember, we're forced into the paradigm of either returning something or not with C# and we must use different delegates for it.
public static TResult ForwardFunc<TArg1, TArg2, TResult>(this TArg1 arg1, Func<TArg1, TArg2, TResult> func, TArg2 arg2)
return func(arg1, arg2);
var result = 4.ForwardFunc((x, y) => x * y, 3);
What I've been able to do is not as clean as F#, but a little interesting. What I'm doing is taking the first argument, then a function to process it, and then finally pass in my second argument. Below that function is a quick and dirty example of how to use it. But what about those times I don't want to return a value? Well, we have an answer for that as well:
public static void ForwardAction<TArg1, TArg2>(this TArg1 arg1, Action<TArg1, TArg2> func, TArg2 arg2)
Enumerable.Range(1, 10).ForwardAction((x, y) => x.ShouldContain(y), 3);
What I did is change the Func<TArg1, TArg2, TResult> to a simple Action<TArg1, TArg2> which has the same effect. From there, I was able to use the xUnit.net 3.5 extensions to show that the IEnumerable<T> contained the number 3. If you mix in currying to this equation things get interesting quickly. I hope those who attended my functional C# class got some inkling, but since then I've been exploring more aspects of it as well as lessons learned. But the overall story is that it's just not as expressive as F# and kind of clumsy.
Extending the Syntax
As you may see, there really isn't much to FsUnit. Instead, I would like to apply some of those lessons to xUnit.net
instead because of allowing static tests and it better fits my needs at the moment. Instead of relying on the underlying unit testing framework to take care of a lot of the details, instead, I'm going to use functional programming aspects of currying to make my DSL look more readable.
The first piece of the puzzle that we need is the basis of getting started. Let's put down the should and a few housekeeping items:
module Extensions =
let should f actual =
What I have created is a module under the FsXUnit namespace called Extensions which will hold my methods. Then I can reference using a standard open statement such as open FsXUnit.Extensions. Then the should will be a partially applied function that will tie the functions together. Now we can start throwing our first functions at it. I'll mark each by section giving you a quick sample of each.
let equal expected actual =
let not_equal expected actual =
let equal_with_equal_values_should_be_equal() =
"foo" |> should equal "foo"
let not_equal_with_inequal_values_should_not_be_equal() =
"foo" |> should not_equal "bar"
let have expected actual =
Assert.Contains(expected, (actual :> seq<_>))
let sequence_should_have_2() =
[1..10] |> should have 2
let throw_exception<'a when 'a :> exn> actual =
let throws_exception() : unit =
raise(System.ArgumentException "Bad things")
let function_should_throw_exception () =
throws_exception |> should (throw_exception<System.ArgumentException>)
This by no means is a complete list of things that you can do with this, but you get the idea.
Where To Go?
As I stated before, I think Scala Specs
is an interesting framework for Behavior Driven Development (BDD), and with some of these techniques discussed, it could be rather possible to make a rather expressive framework. It's all about the time for tinkering at this point I suppose. But with all this tinkering I've been doing, I have to wonder to myself, whether we need another testing DSL, or should we more focus on a general purpose language that is best suited for testing, call it Test#, or Fact#, just to humor Brad Wilson
... Is that where we should be headed?