tags:

views:

203

answers:

4

I know virtually nothing about F#. I don’t even know the syntax, so I can’t give examples.

It was mentioned in a comment thread that F# can declare functions that can take parameters of multiple possible types, for example a string or an integer. This would be similar to method overloads in C#:

public void Method(string str) { /* ... */ }
public void Method(int integer) { /* ... */ }

However, in CIL you cannot declare a delegate of this form. Each delegate must have a single, specific list of parameter types. Since functions in F# are first-class citizens, however, it would seem that you should be able to pass such a function around, and the only way to compile that into CIL is to use delegates.

So how does F# compile this into CIL?

+3  A: 

Assuming I understand the question, in F# you can define expressions which depend on the availability of members with particular signatures. For instance

let inline f x a = (^t : (member Method : ^a -> unit)(x,a))

This defines a function f which takes a value x of type ^t and a value a of type ^a where ^t has a method Method taking an ^a to unit (void in C#), and which calls that method. Because this function is defined as inline, the definition is inlined at the point of use, which is the only reason that it can be given such a type. Thus, although you can pass f as a first class function, you can only do so when the types ^t and ^a are statically known so that the method call can be statically resolved and inserted in place (and this is why the type parameters have the funny ^ sigil instead of the normal ' sigil).

Here's an example of passing f as a first-class function:

type T() = 
  member x.Method(i) = printfn "Method called with int: %i" i

List.iter (f (new T())) [1; 2; 3]

This runs the method Method against the three values in the list. Because f is inlined, this is basically equivalent to

List.iter ((fun (x:T) a -> x.Method(a)) (new T())) [1; 2; 3]

EDIT

Given the context that seems to have led to this question (C# - How can I “overload” a delegate?), I appear not to have addressed your real question at all. Instead, what Gabe appears to be talking about is the ease with which one can define and use discriminated unions. So the question posed on that other thread might be answered like this using F#:

type FunctionType = 
| NoArgument of (unit -> unit)
| ArrayArgument of (obj[] -> unit)

let doNothing (arr:obj[]) = ()
let doSomething () = printfn "'doSomething' was called"

let mutable someFunction = ArrayArgument doNothing
someFunction <- NoArgument doSomething

//now call someFunction, regardless of what type of argument it's supposed to take
match someFunction with
| NoArgument f -> f()
| ArrayArgument f -> f [| |] // pass in empty array

At a low level, there's no CIL magic going on here; it's just that NoArgument and ArrayArgument are subclasses of FunctionType which are easy to construct and to deconstruct via pattern matching. The branches of the pattern matching expression are morally equivalent to a type test followed by property accesses, but the compiler makes sure that the cases have 100% coverage and don't overlap. You could encode the exact same operations in C# without any problem, but it would be much more verbose and the compiler wouldn't help you out with exhaustiveness checking, etc.

Also, there is nothing here which is particular to functions; F# discriminated unions make it easy to define types which have a fixed number of named alternatives, each one of which can have data of whatever type you'd like.

kvb
I tried the code and it works. However, I do not understand `f` on the syntactical level. The function looks like it would return a tuple `(x,a)`. But it actually somehow manages to call `Method`?
wmeyer
@wmeyer - yeah, the syntax for member constraints is a bit confusing. In this case we're calling an instance method called `Method` on type `^t`, having signature `^a -> unit`. Because this is an instance method, we call it by passing in both the instance `x` and the argument `a` (these are inputs to the method, not a return value). So `f x a` is equivalent to `x.Method(a)`, and `f` has type `^t -> ^a -> unit`.
kvb
@kvb - thanks! I think I understand it now. The code both constrains x AND names the method to call (with somewhat special method calling syntax).
wmeyer
@wmeyer - yes, that's correct. The syntax is a bit hard to follow, so I'd only use inline methods like this if there's a really compelling reason. Most of the time you'd just use interfaces or base classes as in other .NET languages, in which case there's no need to use this kind of member constraint.
kvb
kvb: You created a type that can hold a function that takes `unit` or a function that takes `obj[]`. The question was about how F# handles a function that can take either `unit` or `obj[]`.
Gabe
@Gabe - if you look at unkiwii's original question, it looks to me like he wants to create a "delegate" instance which could wrap a method of type `unit -> unit` or one of type `object[] -> unit` (his existing `DoSomething` and `DoNothing` methods), although it's not totally clear. This is what I was trying to address, not your subsequent conversation with Timwi regarding functions which could take either a `string` or an `int`.
kvb
+1  A: 

I'm not quite sure that understand your question correctly... F# compiler uses FSharpFunc type to represent functions. Usually in F# code you don't deal with this type directly, using fancy syntactic representation instead, but if you expose any members that returns or accepts function and use them from another language, line C# - you will see it. So instead of using delegates - F# utilizes its special type with concrete or generic parameters. If your question was about things like add something-i-don't-know-what-exactly-but-it-has-addition-operator then you need to use inline keyword and compiler will emit function body in the call site. @kvb's answer was describing exactly this case.

desco
Having been the person who prompted this question, I know that you understood it and gave the correct answer.
Gabe
Hm, I am not convinced. The declaration for that class is (in C# syntax) `public abstract class FSharpFunc<T, TResult>`. Clearly this can take only one type `T` at a time. While it is interesting that F# has this extra class to represent functions, it doesn’t answer the question. (From the other answers, it seems that the real answer is “Gabe gave a very simplified picture when they said F# could have such functions”.)
Timwi
Timwi: In languages like F#, functions only take one parameter (which is curried). If you call `f(x, y)` you are calling `f` with the single parameter of the tuple `x, y`; if you call `f x y` you are calling a `f` with the parameter `x` which creates a new function that you are calling with the parameter `y`.
Gabe
See also `Core.Choice<T1, T2, ...>`: http://msdn.microsoft.com/en-us/library/ee370523.aspx
Gabe
@Gabe - you can define delegates that act as curried functions in C# too - they would have type `Func<A,Func<B,C>>`, and could be called as `f(a)(b)`. Although the underlying infrastructure in F# is designed to make curried functions efficient, from a conceptual level there's nothing dramatically different from C#.
kvb
kvb: Timwi isn't asking about how it works on the conceptual level -- he wants to know how the underlying infrastructure works.
Gabe
@Gabe - see my comments under @Brian's answer; if what you are really talking about is discriminated unions, then that is only tangential to how functions work in F#. There's nothing preventing you from using a lambda in C# to create a `Func<Choice2<int,string>,R>`, though getting the values out without using pattern matching is mildly annoying.
kvb
+8  A: 

This question is a little ambiguous, so I'll just ramble about what's true of F#.

In F#, methods can be overloaded, just like C#. Methods are always accessed by a qualified name of the form someObj.MethodName or someType.MethodName. There must be context which can statically resolve the overload at compile-time, just as in C#. Examples:

type T() =
    member this.M(x:int) = ()
    member this.M(x:string) = ()
let t = new T()
// these are all ok, just like C#
t.M(3)
t.M("foo")
let f : int -> unit = t.M
let g : string-> unit = t.M
// this fails, just like C#
let h = t.M // A unique overload for method 'M' could not be determined 
            // based on type information prior to this program point.

In F#, let-bound function values cannot be overloaded. So:

let foo(x:int) = ()
let foo(x:string) = ()  // Duplicate definition of value 'foo'

This means you can never have an "unqualified" identifier foo that has overloaded meaning. Each such name has a single unambiguous type.

Finally, the crazy case which is probably the one that prompts the question. F# can define inline functions which have "static member constraints" which can be bound to e.g. "all types T that have a member property named Bar" or whatnot. This kind of genericity cannot be encoded into CIL. Which is why the functions that leverage this feature must be inline, so that at each call site, the code specific-to-the-type-used-at-that-callsite is generated inline.

let inline crazy(x) = x.Qux(3) // elided: type syntax to constrain x to 
                               // require a Qux member that can take an int
// suppose unrelated types U and V have such a Qux method
let u = new U()
crazy(u) // is expanded here into "u.Qux(3)" and then compiled
let v = new V()
crazy(v) // is expanded here into "v.Qux(3)" and then compiled

So this stuff is all handled by the compiler, and by the time we need to generate code, once again, we've statically resolved which specific type we're using at this callsite. The "type" of crazy is not a type that can be expressed in CIL, the F# type system just checks each callsite to ensure the necessary conditions are met and inlines the code into that callsite, a lot like how C++ templates work.

(The main purpose/justification for the crazy stuff is for overloaded math operators. Without the inline feature, the + operator, for instance, being a let-bound function type, could either "only work on ints" or "only work on floats" or whatnot. Some ML flavors (F# is a relative of OCaml) do exactly that, where e.g. the + operator only works on ints, and a separate operator, usually named +., works on floats. Whereas in F#, + is an inline function defined in the F# library that works on any type with a + operator member or any of the primitive numeric types. Inlining can also have some potential run-time performance benefits, which is also appealing for some math-y/computational domains.)

Brian
This answer is pretty good. Despite the ambiguity of the question, it gave a pretty complete picture of functions in F#.
Timwi
On the contrary, I'd say it was far from complete.
Gabe
No description of F# functions is complete without discriminated unions (http://msdn.microsoft.com/en-us/library/dd233226.aspx) and pattern matching (http://msdn.microsoft.com/en-us/library/dd233242.aspx)
Gabe
@Gabe - while pattern matching and discriminated unions are awesome features, I think that they are orthogonal to functions. If you've got a function of type `T -> U`, the language works the same whether `T` is a discriminated union type or not.
kvb
kvb: You don't understand the context. Somebody posted a question asking how to create a delegate in C# that can accept different sets of formal parameters like overloaded functions can (e.g. *either* an int or a string). Timwi suggested that needed to pass around a function with disjoint sets of parameters was probably an indication of poor design. I disgreed, saying that it is easy to do such a thing in F# (indeed, it's commonplace). Thus, Timwi posted this question asking how it works.
Gabe
@Gabe - I was just taking issue with your statement that "No description of F# functions is complete without discriminated unions and pattern matching". It may be that the original question in this thread was motivated by a good use case for a discriminated union; however, that really doesn't have anything to do with functions in F#. I think that Brian's explanation did a good job of covering the relevant aspects of F# methods and functions.
kvb
@Gabe - Additionally, it's not strictly true that you can define functions in F# could take either a string or an int. You can certainly define a function which could take a `Choice2<int,string>`, but that's not quite the same - to call it you need to explicitly create either a `Choice1Of2` value containing an int or a `Choice2Of2` value containing a string, you can't just use raw values.
kvb
+2  A: 

When you're writing C# and you need a function that can take multiple different parameter sets, you just create method overloads:

string f(int x)
{
    return "int " + x;
}
string f(string x)
{
    return "string " + x;
}
void callF()
{
    Console.WriteLine(f(12));
    Console.WriteLine(f("12"));
}
// there's no way to write a function like this:
void call(Func<int|string, string> func)
{
    Console.WriteLine(func(12));
    Console.WriteLine(func("12"));
}

The callF function is trivial, but my made-up syntax for the call function doesn't work.

When you're writing F# and you need a function that can take multiple different parameter sets, you create a discriminated union that can contain all the different parameter sets and you make a single function that takes that union:

type Either = Int of int
            | String of string
let f = function Int x -> "int " + string x
               | String x -> "string " + x

let callF =
    printfn "%s" (f (Int 12))
    printfn "%s" (f (String "12"))

let call func =
    printfn "%s" (func (Int 12))
    printfn "%s" (func (String "12"))

Being a single function, f can be used like any other value, so in F# we can write callF and call f, and both do the same thing.

So how does F# implement the Either type I created above? Essentially like this:

public abstract class Either
{
    public class Int : Test.Either
    {
        internal readonly int item;
        internal Int(int item);
        public int Item { get; }
    }
    public class String : Test.Either
    {
        internal readonly string item;
        internal String(string item);
        public string Item { get; }
    }
}

The signature of the call function is:

public static void call(FSharpFunc<Either, string> f);

And f looks something like this:

public static string f(Either _arg1)
{
    if (_arg1 is Either.Int)
        return "int " + ((Either.Int)_arg1).Item;
    return "string " + ((Either.String)_arg1).Item;
}

Of course you could implement the same Either type in C# (duh!), but it's not idiomatic, which is why it wasn't the obvious answer to the previous question.

Gabe