On Tue, Mar 24, 2009 at 6:02 PM, Peter Verswyvelen <bugfact@gmail.com> wrote:


2009/3/24 Brandon S. Allbery KF8NH <allbery@ece.cmu.edu>

That's my understanding, F++ types don't require any runtime lookups so inference can't surprise you.  

It uses .NET interfaces for that, but the F# expert book mentions they would like to have type classes in a future version of the language.
 
in Haskell can do unexpected things:  F++ has parenthesized function arguments, so it will catch too few/too many arguments directly, but Haskell's uncurried function call notation almost guarantees strange errors in those cases even if you have everything explicitly typed.

Not really, F# has both curried and uncurried forms, just like Haskell.

It is true that F# encourages automatic generalization without type signatures. You never need to give one, even for exported modules.

But the lack of type classes has its price. E.g.

add x y = x + y

What is the type of add in F#?

Well, that depends on the first usage of this function... Give it Ints and it becomes add :: Int -> Int -> Int, and you can't use it on Doubles any more. Very confusing, since you can use the overloaded (+) on any type. These are current limitations of the language.

At least that what I understood from it...


What you say is partially true.  For example, if delcared as you typed it, a rule similar to the monomorphism restriction (from what I understand of the monomorphism restriction) applies.  If it's never used, x and y are of type int, and if they are used, x and y pick up the type of whatever you call it with.  So doubles if you invoke it with 3.0 and 4.0, ints if you invoke it with 3 and 4, and an error if you invoke it once with doulbes and once with ints.  

The F# answer to this is to "inline" the function.  For example:

let inline add (x:'a) (y:'a) : 'a = x+y

let result1 = add 3 4
let result2 = add 3.0 4.0

Hover over this declaration of add to get its type and you see "add : 'a -> 'a -> 'a (requires member (+))

Here, result1 is an int and result2 is a double.  I haven't heard this from straight from the developers, but I suspect inlining is necessary due to the .NET Framework's support for reflection.  Consider, for example, what would happen if you tried to obtain the method add via reflection and there was code for exactly 1 add method present in the binary.  The code would have no reasonable way to behave.  By inlining, I believe it inserts multiple definitions of hte function into the code, similar to what you get when you instantiate a C++ template. 

Anyway, a bit off topic, but at the very least I'm starting to understand why it's ok in F# but not really so much in Haskell.  The issue of documenting the function I think is kind of a non issue actually, because if it were really just for the purposes of documentation you could just comment out the the type signature so that it's still visible but not interpreted by the compiler.  In F# though the documentation is even less of an issue since you get automatic mouse-hover type definitions.  The same thing applies to the issue of detecting type errors.  It's pretty easy to find them when all the type inference mismatches are underlined for you on the fly.  But in Haskell it seems the problem of finding type errors is made more difficult not only by the lack of a helpful syntax underliner, but also by the typeclasses, which make the errors even more far removed from what you would expect.  Not to mention the performance penalties associated with making something too generic, which I didn't think of originally.