1

The next test fail. I call GetType directly to a function definition, and then I also call GetType within an inline function. The generated types are not equal.

namespace PocTests

open FsUnit
open NUnit.Framework

module Helpers = 
    let balance ing gas = ing - gas

[<TestFixture>] 
type ``Reflected types`` ()=

    [<Test>] member x.
        ``test type equality with inline use`` () =
            let inline (=>) f = f.GetType().FullName, f in
            let fullName, fval = (=>) Helpers.balance in
            Helpers.balance.GetType().FullName |> should equal (fullName)

How could I get the same type in order to be "comparable".

jruizaranguren
  • 12,679
  • 7
  • 55
  • 73

2 Answers2

6

When you use a function as a value, F# does not give you any guarantees that the two created objects will be the "same". Under the cover the compiler creates a new closure object for each instance, so you will actually get false as the result even when you try something like this:

balance.GetType().FullName = balance.GetType().FullName

This is the intended behavior - when you try comparing functions directly, the compiler will tell you that functions do not satisfy the equality constraint and cannot be compared:

> let balance ing gas = ing - gas;;
val balance : ing:int -> gas:int -> int

> balance = balance;;
error FS0001: The type '(int -> int -> int)' does not support the 
'equality' constraint because it is a function type

This means that the best answer to your question is that what you're asking for cannot be done. I think that comparing function values is most likely not a good idea, but perhaps there is a better answer for your specific problem if you provide some more details why you want to do this.

If you really want to perform equality testing on function values, then probably the cleanest approach is to define an interface and test ordinary object equality:

type IFunction = 
  abstract Invoke : int * int -> int
let wrap f = 
  { new IFunction with
      member x.Invoke(a, b) = f a b }

Now you can wrap the balance function in an interface implementation that can be compared:

let balance ing gas = ing - gas
let f1 = wrap balance
let f2 = f1
let f3 = wrap balance
f1 = f2 // These two are the same object and are equal
f1 = f3 // These two are different instances and are not equal
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • I'm getting by reflection a map where spec is (function type, name, args, return type). Also I'm building a mapobj*string> with a selection of those functions. I wanted to enforce a convention to ensure that for the same key I get a function that corresponds to the spec (because function map is made by hand). But if Types are different it does not seem feasible as you say. – jruizaranguren Sep 10 '14 at 21:48
  • It would be enough for my purpose If I could wrap the functions in a quotation in order to get the name or signature. But according to your comment in http://stackoverflow.com/questions/4944668/f-quotations-traversing-into-function-calls-represented-by-a-value this is not feasible, unless some new hack is available after 2011. – jruizaranguren Sep 11 '14 at 10:32
1

every time you call Helpers.balance a new closure is created, so

Helpers.balance.GetType().FullName |> printfn "%A" //output: "Program+main@22-1" 
Helpers.balance.GetType().FullName |> printfn "%A" //output: "Program+main@23-2" 

with class like (decompiled from compiled exe in c#)

[Serializable]
internal class main@22-1 : OptimizedClosures.FSharpFunc<int, int, int>
{
    internal main@22-1()
    {
      base..ctor();
    }

    public override int Invoke(int ing, int gas)
    {
      return Program.Helpers.balance(ing, gas);
    }
}
Enrico Sada
  • 909
  • 4
  • 9