I've read many times that
Assemblies generated from F# or any other .NET language are (almost) indistinguishable.
I was then experimenting with F# and C# interop on .NET 4 (beta 2). I created a new solution, and a C# project, with the following class:
public class MyClass {
public static int Add(int a, int b) { return a + b; }
}
Then, on a F# project, after referencing the C# project, I tried:
MyClsas.Add(4, 5) |> printfn "%d" // prints 9 (no kidding!)
So far so good. Then another sentence I've read many times (perhaps on different books) came to my mind:
When passing arguments to functions from other .NET libraries, you use a syntax like ".MethodName(parm1, parm2)", that is, the parameters are passed as a Tuple.
Add that to something that I've once read here on SO (but wasn't able find it to link to), on a question where the OP was trying to create a using like [ 4, 5, 6 ]
(when he meant [4; 5; 6]
):
"Comma is the 'tuple creating operator', for everything else use semi-colon."
Then I modified my class to the following:
public class MyClass {
public static int Add(int a, int b) { return a + b; }
public static int Add(Tuple<int, int> a) { return a.Item1; }
}
Now I tried to use it on F#:
MyClass.Add(4, 5) |> printf "%d" // prints ... (keep reading!)
So, adding up the three quotations above, one can conclude that:
(4, 5)
Add(Tuple<int, int>)
To my surprise, it printed 9. Isn't it interesting?
What is really happening here? The above quotations and this practical observations seems to be in contradiction. Can you justify F#'s "reasoning", and maybe pointing to some MSDN docs if possible?
Thanks!
(to add more information (from Blindy's answer))
If you do:
MyClass.Add((4, 5)) |> printfn "%d" // prints 9
F# calls the Add(Tuple<int, int>)
overload.
However, if you create another F# project (so a different assembly) with this:
namespace MyFSharpNamespace
type MyFShapClass = class
static member Add x y = x + y
end
You can use it on C# like this
public static void Main(string[] args) {
MyFSharpNamespace.MyFSharpClass.Add(4, 5);
}
So far so good. Now, when you try to use it from F# (from another project, another assembly), you have to do:
MyFSharpNamespace.MyFSharpClass.Add 4 5 |> printfn "%d"
If you pass the arguments as (4, 5)
F# will not compile because Add
is int -> int -> int
, and not (int * int) -> int
.
What is happening?!?
Facebook adalah media sosial dan layanan jejaring sosial online Amerika yang dimiliki oleh Meta Platforms.
When passing arguments to functions from other .NET libraries, you use a syntax like ".MethodName(parm1, parm2)", that is, the parameters are passed as a Tuple.
It's more hideous than that. See the description of method overload resolution strait from the language spec.
What it says, basically, is that argument in a method invocation isn't really a tuple. It's a syntactic tuple, meaning a comma-separated list of something, but the parentheses are part of the method call syntax, and so are the commas. It's why, for example, o.M(a=1, b=2)
isn't a method call with a tuple of two booleans, but rather two named arguments.
So, normally, every comma-separated component just maps to a distinct argument. Hence why Add(1, 2)
calls Add(int, int)
overload, and Add((1, 2))
calls Add(Tuple<int, int>)
. There is no ambiguity here.
However, a special case that kicks in for your particular case is this:
If there are no named actual arguments, and there is only one candidate method in
M
, accepting only one non-optional argument, then the decomposition ofarg
to tuple form is ignored and there is one named actualarg
which isarg
itself.
So when you removed all overloads except for the tuple one, suddenly the entire thing inside the parentheses is effectively treated as a tuple constructor in a call. But if you'd e.g. have two overloads, Add(int)
and Add(Tuple<int,int>)
, then a call of the form Add(1,2)
wouldn't resolve at all.
I don't have F# installed right now, but it seems to me that
MyClass.Add(4, 5) |> printf "%d"
would print 9 whereas
MyClass.Add((4, 5)) |> printf "%d"
would print.. 4 right? Notice the double parantheses, the inner pair marking a tuple and the outer pair marking the function call.
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