have a type provider with three properties 'a', 'b', and 'c' of type 'string', 'string option' and 'int option', respectively.
When I have an instance with "", None, and Some 1 in those properties, this fails:
(row1.a, row1.b, row1.c) |> should equal ("", None, Some 1)
But all of these work fine:
row1.a |> should equal ""
row1.b |> should equal None
row1.c |> should equal (Some 1)
("", None, Some 1) |> should equal ("", None, Some 1)
How is this possible? What can make the None in b be different from any other None? After compilation, None is just a null, can two null values be different in .Net?
Tuples have structural equality, like most F# types, so it should work. I get an NUnit.Framework.AssertionException with Message:
Expected: <(, , Some(1))>
But was: <(, , Some(1))>
NUnit just calls .Equals, so that's where the problem is.
This also fails:
(row1.a, row1.b, row1.c).Equals(("", None, Some 1)) |> should equal true
The runtime type of row1
is System.Tuple<string,Microsoft.FSharp.Core.FSharpOption<string>,Microsoft.FSharp.Core.FSharpOption<int>>
, so even this should work in theory:
row1 |> should equal ("", None, Some 1)
And in fact it does when there's no None
in the tuple.
I can't reproduce this behaviour with anything else but type providers.
We have been bitten by this several times, so we create a specific note on this:
FsUnit uses type test to implement its DSL. Type inference doesn't work on this DSL, so make sure that two compared values belong to the same type.
For example, for some generic values such as True
, False
, etc, you need to specify their types (as formula<fol>.True
, formula<fol>.False
, etc) otherwise these values will be compared as being of type obj
.
If you take a look at how FsUnit is implemented, it isn't really type-safe. I believe Jack P.'s pull request is a step towards making FsUnit more type-safe. It seems to be an area to improve FsUnit.