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
@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 int
s and the second is string
s) - 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 Int
s and one for String
s) - 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