Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are functions bound to the first type they are passed

Tags:

f#

I'm new to F#. I was messing around and I found something interesting and I was hoping someone could enlighten me as to what is going on behind the scenew.

So I made the function: let my_func (x, y) = x + y.

Then I called the function with the args 1 and 2 giving me 3. This is what I expected to happen but when I passed two strings to my_func I got an error even though + is a valid operator with strings. I reran my code but this time only calling my_func with "cat" and " dog" which gave me "cat dog". I then tried to pass 1 and 2 back to my_func only to find that my_func no long accepts integers.

Why is my_func behaving this way?

let my_func (x, y) = x + y
my_func (1, 2) // produces => 3
my_func ("cat", " dog") // Error

rerun program...

let my_func (x, y) = x + y
my_func ("cat", " dog") // produces => "cat dog"
my_func (1, 2) // Error

like image 873
X_Trust Avatar asked Oct 31 '14 05:10

X_Trust


1 Answers

@MarcinJuraszek showed you how to solve this issue but said nothing about why it happens.

You can think of it like this:

F#'s type inference works top to bottom, left to right - so when the system tries to find the type for my_func it will find assign the types from the first line where you are using the function (first example it is ints and the second is strings) - If you don't use it at all or define it in FSharp Interactive it will indeed default to int.

Declaring the function as inline enables F# to use statically resolved type parameters (due to some details this is only possible with inline functions) and then it will indeed do something like duck-typing to figure out from the declaration that the function needs types where a static + operator is defined somehow.

You can see this in the type of the function:

val inline my_func :
  x: ^a * y: ^b ->  ^c
    when ( ^a or  ^b) : (static member ( + ) :  ^a *  ^b ->  ^c)

this rather complicated type says just this:

There must be a static operator (+) : ^a * ^b -> ^c on ^a (think 'a) that is used when you write + in the functions body. As you can see this is even more generic than you really need it but this is no issue. F# will implement concrete versions (with the generic types substituted) for ever occurrence of this function you apply (so in your example there will be two my_func instantiations in your IL; one for Ints and one for Strings) - but this won't bother you at design-time at all.

So you now have a more generic function that can be used with:

  • (+) : Int * Int -> Int on Int
  • (+) : String * String -> String on String
like image 101
Random Dev Avatar answered Nov 15 '22 09:11

Random Dev