Question

I started using FsUnit to test F# code. It makes it possible to express assertion in F# style, for example:

[<Test>]
member this.``Portugal voted for 23 countries in 2001 Eurovision contest``() =
    this.totalVotes 
    |> getYearVotesFromCountry "Portugal" 2001
    |> Seq.length
    |> should equal 23

Note "should equal 23" that I get from FsUnit. Here's how FsUnit defines it:

let equal x = new EqualConstraint(x)

With floating point numbers it's not that simple. I have to use EqualConstraint with Within method. It naturally fits C#:

Assert.That(result).Is.EqualTo(1).Within(0.05);

Of course I'd like to be able to write in F#:

result |> should equal 1 within 0.05

But that does not work. I ended up defining a new function:

let almostEqual x = (new EqualConstraint(x)).Within(0.01)

or if I want to parametrize precision, I can specify it as a second argument:

let equalWithin x y = (new EqualConstraint(x)).Within(y)

But none of them are pretty. I would like to define "within" function in a more natural way for F#, so it can be used together with equal. F# does not support method overloading, so it looks like I can't define it in such a way so "equal" can be used either alone or together with "within".

Any ideas?

Was it helpful?

Solution

This is an interesting problem! I don't think that you can append within 0.05 to the existing definition of should equal in any way. To do that you would need to add parameter to the should function, but that needs to have a fixed number of parameters in the library.

One way to write this elegantly in F# would is to create custom operator +/-. Note that you still need to use parentheses, but it looks quite neat:

0.9 |> should equal (1.0 +/- 0.5)

The operator simply constructs some value of a special type that needs to be explicitly handled in the equal function. Here is the implementation:

type Range = Within of float * float
let (+/-) (a:float) b = Within(a, b)

let equal x = 
  match box x with 
  | :? Range as r ->
      let (Within(x, within)) = r
      (new EqualConstraint(x)).Within(within)
  | _ ->
    new EqualConstraint(x)

OTHER TIPS

Terminology note: F# does support method overloading; it does not support overloading let-bound functions ('free' functions defined in modules).

You could e.g. make it should.equal with a dot, so that equal is a method on the should object, which you could overload. Although that still wouldn't help, since you can't overload curried arguments. Hmmm.

Ok, I don't have much helpful to offer. Personally I don't like these type of syntactic sugars in libraries.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top