views:

102

answers:

3

Given the following code:

type MyType() =
    static member processString (_string:string) = _string.Substring(0, 1)
    static member processInt (_int:int) = _int.ToString()
    static member processItems = List.map MyType.processString
    static member processItems = List.map MyType.processInt

The last two lines will not work. I have to do this:

    static member processItems (_strings:string list) = _strings |> List.map MyType.processString
    static member processItems (_ints:int list) = _ints |> List.map MyType.processInt

Even if I do this, the second line fails:

    static member processItems (_strings:string list) = _strings |> List.map MyType.processString
    static member processItems = List.map MyType.processInt

Since F# is all fancy-pants with the type inference, why can't it figure out that the two processItems member functions have different parameter signatures without my having to explicitly provide parameter types for both of them??

+2  A: 

Somebody who knows more about F# can probably do a better job with this that I can, but I think it has to do with the fact that the signatures of the arguments (i.e. no arguments) of the members (as values) are the same. It's by the return types that they differ, which is not enough to distinguish them. Providing explicit arguments makes it possible to create different input signatures:

type MyType() = 
    static member processString (_string:string) = _string.Substring(0, 1) 
    static member processInt (_int:int) = _int.ToString() 
    static member processFloat (_float:float) = _float.ToString() 
    static member processItems a = a |> List.map MyType.processString  
    static member processItems a = a |> List.map MyType.processInt 
    static member processItems a = a |> List.map MyType.processFloat 

If you know C# or C++, think about how you would create something similar to the original code in those languages. The same problem would obtain.

TechNeilogy
+4  A: 

I believe the issue arises because of the difference between methods and properties at the .NET representation level. Your original code would result in two different static properties with the same name (which is not supported). Your intermediate example defines two methods with the same name but different argument types (which is supported). I'm not sure why the final example doesn't work. Note that I don't think that this has anything to do with inference. This works:

type MyType() = 
    static member processString (_string:string) = _string.Substring(0, 1) 
    static member processInt (_int:int) = _int.ToString() 
    static member processItems l = l |> List.map MyType.processString 
    static member processItems l = l |> List.map MyType.processInt
kvb
The second example that I gave *does* work (as I wrote).Interesting that your example works! Your explanation that it is a .NET issue sounds plausible. Thanks for the eye-opening response!
MiloDC
@MiloDC - oops, I switched up "intermediate" and "final" in my response. I've edited the answer.
kvb
+1  A: 

The cause of issue here lies not in type inference. In your first sample use define two properties that has the same names but different return types.

type MyType() =
    static member processString (_string:string) = _string.Substring(0, 1)
    static member processInt (_int:int) = _int.ToString()
    static member processItems = List.map MyType.processString
    static member processItems = List.map MyType.processInt

second sample is correct because you explicitly declare two methods

type MyType() =
    static member processString (_string:string) = _string.Substring(0, 1)
    static member processInt (_int:int) = _int.ToString()
    static member processItems (_strings:string list) = _strings |> List.map MyType.processString
    static member processItems (_ints:int list) = _ints |> List.map MyType.processInt

you can modify it by removing type annotations:

type MyType() =
    static member processString (_string:string) = _string.Substring(0, 1)
    static member processInt (_int:int) = _int.ToString()
    static member processItems s = s |> List.map MyType.processString
    static member processItems i = i |> List.map MyType.processInt

in the third sample you are trying to define property and method with the same names (C# prohibits this too, through IIRC it is not forbidden by CLI spec)

type MyType() =
    static member processString (_string:string) = _string.Substring(0, 1)
    static member processInt (_int:int) = _int.ToString()
    static member processItems (_strings:string list) = _strings |> List.map MyType.processString
    static member processItems = List.map MyType.processInt
desco
Understood, I thought I was employing partial application to define methods, but I was in fact just defining properties. Gotcha, that clears it up! Thanks.
MiloDC