F # generics / syntax overload function

I am confused about how to designate a function as universal without explicit type declaration, e.g. ('a -> 'a)

 let add ab = a + b 

It gives us

 val add : a:int -> b:int -> int 

However, we can immediately call

 add "Hello " "World!" 

and now the value is add

 val add : a:string -> b:string -> string val it : string = "Hello World!" 

If we then call

 add 2 3 // then we get error: This expression was expected to have type string but here has type int 

How can I guarantee that a function works on all types that say that a function (+) defined

+10
function generics types f #
May 25 '15 at 20:50
source share
3 answers

This is the inconvenient skeleton of F # in the closet.

Try the following:

 > let mapPair f (x,y) = (fx, fy) val mapPair : f:('a -> 'b) -> x:'a * y:'a -> 'b * 'b 

Totally generic! Obviously the application application and tuples work.

Now try the following:

 > let makeList ab = [a;b] val makeList : a:'a -> b:'a -> 'a list 

Hmmm, also common. How about this:

 > let makeList ab = [a + b] val makeList : a:int -> b:int -> int list 

Yeah, as soon as I have (+) , for some reason it becomes int .
Keep playing:

 > let inline makeList ab = [a + b] val inline makeList : a: ^a -> b: ^b -> ^c list when ( ^a or ^b) : (static member ( + ) : ^a * ^b -> ^c) 

Hmmm, interesting. It turns out that if I make the inline function, then F # considers it to be common, but it also gives it this strange when clause, and my common parameters have this strange ^ symbol instead of the usual tick.
This strange syntax is called "statically permitted type parameters" (see here for a somewhat consistent explanation), and the main idea is that the function (+) requires its arguments to have static member (+) . Let it be checked:

 > let x = 0 :> obj let y = 0 :> obj let z = x + y Script1.fsx(14,13): error FS0001: The type 'obj' does not support the operator '+' > type My() = static member (+)( a:My, b:My ) = My() let x = My() let y = My() let z = x + y val x : My val y : My val z : My 

Now the problem is that the CLR does not support such common parameters (that is, "any type if it has such and such members"), so F # must fake it and allow these calls at compile time. But because of this, any methods that use this function cannot be compiled for true general IL methods and, therefore, must be monomorphic (which is included inline ).

But then it would be very inconvenient to require that every function using arithmetic operators be declared inline, wouldn't it? Thus, F # performs another additional step and tries to fix these statically permitted common parameters based on how they are created later in the code. Therefore, your function turns into string->string->string as soon as you use it with string once.

But if you mark your inline function, F # will not need to fix the parameters, because it will not need to compile the function before IL, and therefore your parameters remain unchanged:

 > let inline add ab = a + b val inline add : a: ^a -> b: ^b -> ^c when ( ^a or ^b) : (static member ( + ) : ^a * ^b -> ^c) 
+16
May 25 '15 at 21:42
source share

If I understand you correctly, use inline:

 let inline add ab = a + b add 2 3 |> printfn "%A" add "Hello " "World!" |> printfn "%A" 

Print

 5 "Hello World!" 

Link: http://ideone.com/awsYNI

+2
May 25 '15 at 21:24
source share

Make inline

 let inline add ab = a + b (* val inline add : a: ^a -> b: ^b -> ^c when ( ^a or ^b) : (static member ( + ) : ^a * ^b -> ^c) *) add "Hello " "World!" // val it : string = "Hello World!" add 2 3 // val it : int = 5 
+2
May 25 '15 at 21:28
source share



All Articles