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 + ymy_func (1, 2) // produces => 3 my_func ("cat", " dog") // Error
rerun program...
let my_func (x, y) = x + ymy_func ("cat", " dog") // produces => "cat dog"my_func (1, 2) // Error
@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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With