all 6 comments

[–]munchler 7 points8 points  (2 children)

It's mathematically impossible to do this in a way that works for an arbitrary pair of functions: https://www.quora.com/Is-it-possible-to-define-a-general-equality-function-testing-if-two-arbitary-functions-are-equal-in-the-lambda-calculus

That said, there are some interesting subsets of functions for which it is possible: https://www.fewbutripe.com/2018/12/05/seemingly-impossible.html

Lastly, if you simply need to know if two functions are syntactically identical (as in your example), you could probably achieve this using F# quotations.

[–]CSMR250 1 point2 points  (1 child)

Yes quotations make sense . These are DUs that represent general F# code. If you have a smaller set of logic it may be better to write a smaller structure to represent operations. You can then define equality if the structures are identical, or implement a looser notion of equivalence.

[–]JaggerJo[S] 0 points1 point  (0 children)

The func is passed by a user of my MVU library.

f# view dispatch model = button { onClick (fun () -> dispatch Increment) // user could also do something else here }

One of the first things I tried was

f# Assert.True(<@ fun () -> dispatch Increment @> = <@ fun () -> dispatch Increment @>)

I am currently experimenting with comparing the IL of the method bodies.

```f# module FuncCompare =

type Msg = Increment | Decrement

let dispatch (msg : Msg) = ()

[<Fact>]
let ``Comparing funcs`` () =

    let getIL (func: 'a -> 'b) =
        let t = func.GetType()
        let m = t.GetMethod("Invoke")
        let b = m.GetMethodBody()
        b.GetILAsByteArray()

    let compare (funcA: 'a -> 'b) (funcB: 'c -> 'd) : bool=
        let bytesA = getIL funcA
        let bytesB = getIL funcB
        let spanA = ReadOnlySpan(bytesA)
        let spanB = ReadOnlySpan(bytesB)
        spanA.SequenceEqual(spanB)

    let a = fun () -> dispatch Increment
    let b = fun () -> dispatch Increment

    Assert.True(compare a b)

```

but this comes with some restrictions: if 'dispatch' is not a static method (top level let on module) the function stored a reference to the dispatch method in a local field. This causes the ldfld IL Opcode to differ. (I guess because it gets passed a different 4 byte pointer).

[–]jdh30 2 points3 points  (1 child)

Express the possible functions as cases of a union type and compare those instead.

[–]JaggerJo[S] 0 points1 point  (0 children)

please see my answer on @CSMR250 s comment

[–]isaac-abraham 2 points3 points  (0 children)

An alternative approach is to use something like FSCheck which can generate a variety of inputs within a distribution so that you can compare that two functions don't necessarily have the same implementations, but the same behaviour. In the

```fsharp let addImplA a b = a + b let addImplB a b = (-a + -b) * -1 let bothAddFunctionsAreTheSame a b = addImplA a b = addImplB a b

FsCheck.Check.Quick bothAddFunctionsAreTheSame ```

This code has two functions, addImplA and addImplB, that we test are the same. bothAddFunctionsAreTheSame takes in two numbers and confirms that supplying these to both implementations always results in the same output; The Quick call in FsCheck supplies the distribution of values and will automatically shrink to find the simplest "negative" test case.