2

I am using FsUnit.Xunit. I am getting a failure for the following test case:

[<Fact>]
let ``Initialization of DFF`` () =
    dff Seq.empty Seq.empty |> should equal (seq {Zero})

The test failure is:

  Message: 
FsUnit.Xunit+MatchException : Exception of type 'FsUnit.Xunit+MatchException' was thrown.
Expected: Equals seq [Zero]
Actual:   seq [Zero]

  Stack Trace: 
That.Static[a](a actual, IMatcher`1 matcher)
Signal.Initialization of DFF() line 11

I get the same error if the test is:

[<Fact>]
let ``Initialization of DFF`` () =
    dff Seq.empty Seq.empty |> should equal (Seq.singleton Zero)

I have never tested equality of sequences using FsUnit.Xunit, so I am confused what's going on. I'm not even for sure what the failure message is telling me, as it seems to be saying that the expected and actual are the same. I can get this to work fine by converting the sequences to lists, but it would be nice to not have to do that.

Could someone explain what's going on here? It seems I'm not understanding the error message and thus probably something about Equals and comparing sequence values (literals?). Thanks.


Source code to be able to reproduce (I think this is everything):

type Bit =
    | Zero
    | One

type Signal = seq<Bit>

let Nand a b =
    match a, b with
    | Zero, Zero -> One
    | Zero, One  -> One
    | One,  Zero -> One
    | One,  One  -> Zero

let Not input =
    Nand input input

let And a b =
    Not (Nand a b)

let Or a b =
    Nand (Not a) (Not b)

let private liftToSignal1 op (signal: Signal) : Signal =
    Seq.map op signal

let private liftToSignal2 op (signalA: Signal) (signalB: Signal) : Signal =
    Seq.map2 op signalA signalB

let Not' = liftToSignal1 Not

let And' = liftToSignal2 And

let Or' = liftToSignal2 Or

let rec dff data clock : Signal =
    seq {
        yield Zero
        yield! Or' (And' data clock)
                        (And' (dff data clock) (Not' clock))
    }
bmitc
  • 357
  • 1
  • 9
  • Is `dff` your custom function? Could you include that code as well, so we can try and reproduce this? – Aage Sep 16 '22 at 06:04
  • @Aage Yea, it's my own function. I was hoping this was just some quirk with sequences and FsUnit or something I'm missing in FsUnit since it's a DSL that isn't always clear on top of another DSL in Xunit. But I went ahead and updated my question to include the required source code to hopefully reproduce the test failure. – bmitc Sep 16 '22 at 06:45

1 Answers1

4

This is an issue with structural vs. referential equality. In F# seq { 'a' } = seq { 'a' } // false but [ 'a' ] = [ 'a' ] // true due to seq being IEnumerable and not supporting structural equality (or comparison). Lists (and other F# container-like types) are much more 'intelligent', i.e. they support structural equality / comparison if the contained objects support it:

[ {| foo = StringComparison.Ordinal; bar = Some(1.23) |} ] =
  [ {| foo = StringComparison.Ordinal; bar = Some(1.23) |} ] // true

but don't, if they contain anything that doesn't: [ box(fun() -> 3) ] = [ box(fun() -> 3) ] // false

So, to make the test work, add a List.ofSeq:

dff Seq.empty Seq.empty |> List.ofSeq |> should equal [ Zero ]
CaringDev
  • 8,391
  • 1
  • 24
  • 43
  • Thank you! That makes sense and what I was missing. I haven't ever used sequences that much in F# other than casting to get some higher-order functions, so that slipped by me. – bmitc Sep 16 '22 at 15:43
  • Is there any difference between `List.ofSeq` or `Seq.toList`? – bmitc Sep 16 '22 at 15:44
  • 1
    @bmitc no, that’s purely a matter of taste. Choose whatever reads better to you. – CaringDev Sep 16 '22 at 16:07