views:

152

answers:

1

Why does...

type IntDelegate = delegate of int -> unit

type ListHelper =
    static member ApplyDelegate (l : int list) (d : IntDelegate) =
        l |> List.iter (fun x -> d.Invoke x)

ListHelper.ApplyDelegate [1..10] (fun x -> printfn "%d" x)

not compile, when:

type IntDelegate = delegate of int -> unit

type ListHelper =
    static member ApplyDelegate (l : int list, d : IntDelegate) =
        l |> List.iter (fun x -> d.Invoke x)

ListHelper.ApplyDelegate ([1..10], (fun x -> printfn "%d" x))

does?

The only difference that is that in the second one, ApplyDelegate takes its parameters as a tuple.

Error 1 This function takes too many arguments, or is used in a context where a function is not expected
+6  A: 

I haven't looked at the spec to confirm, but I am guessing that the implicit conversion from "lambda" to "named delegate type" only occurs in a "member invocations".

You can always make the conversion explicit:

ListHelper.ApplyDelegate [1..10] (IntDelegate(fun x -> printfn "%d" x))

(The error diagnostic is quite poor; I'll file a bug.)

EDIT:

For the wonks...

Yeah, the spec says

8.13.6 Type-directed Conversions at member invocations As described in Method Application Resolution (see §14.4), two type-directed conversions are applied at method invocations.

If a formal parameter is of delegate type DelegateType, and an actual argument is syntactically a function value (fun ...), then the parameter is interpreted as if it had been written new DelegateType(fun ...).

that lambdas get converted automagically to delegate types only at "member invocations". In the case of the curried member, the first argument passed is a member invocation, but then that returns a function value to apply the second argument, and function invocations do not have this implicit conversion rule.

Brian
Thank you. What exactly is a 'member invocation' and why does having tupled arguments make a difference?Should members always take tupled arguments?
Matt H
"Members" are things you define with the `member` keyword, e.g. static or instance methods on some type. These contrast with "functions" which are defined via `let f x = ...` or `fun` and which are comparatively very strict (e.g. you cannot overload a function, but you can overload a member). Members should probably always take tupled arguments, yes; members are more .NET-y entities, as compared to functions which are F#-specific.
Brian
The 'tupled arguments' makes a difference because when it's tupled, all the arguments are passed 'at once' to the member, so the second argument is an argument to a member. In the case of curried arguments, the first argument is an argument to the member, and that results in a new (non-member) function value, which then takes the second argument.
Brian
Right! Thanks for the clear explanations. :)
Matt H