Reputation: 10350
Being spotted in context of this question this seemingly inconsistent behavior can be reproduced as following both in F#2.0 and F#3.0 RC:
type Heterogeneous =
static member Echo([<ParamArray>] args: Object[]) = args
type Generic =
static member Echo<'T>([<ParamArray>] args: 'T[]) = args
Usage: Returns:
Heterogeneous.Echo 0 // [|0|] OK
Generic.Echo 0 // [|0|] OK
Heterogeneous.Echo (0,1) // [|0; 1|] OK
Generic.Echo (0,1) // [|0; 1|] OK
Heterogeneous.Echo [|0|] // [|[|0|]|] OK?
Generic.Echo [|0|] // [|0|] OOPS!!
Heterogeneous.Echo ([|0|],[|1|])) // [|[|0|]; [|1|]|] OK
Generic.Echo ([|0|],[|1|])) // [|[|0|]; [|1|]|] OK
Can anyone explain if the observed behavior is a bug, or feature?
UPDATE:
This related answer communicates a confirmation from F# development team that as of now a bug has place in processing of generic type arguments with ParamArray
attribute.
Upvotes: 4
Views: 365
Reputation: 243096
The case is slightly confusing because when you use an array as an actual argument to a parameter marked with ParamArray
, the language tries to interpret it as if you were passing array to a normal array-typed parameter (so it ignores the ParamArray
attribute if possible).
In your example, this is possible in the second case:
Generic.Echo [|0|]
The compiler infers that 'T
is int
and so you're passing int[]
to a parameter of type int[]
and so the compiler ignores ParamArray
attribute and the method simply gets an array containing 0
.
In the other case, this is not possible:
Heterogeneous.Echo [|0|]
The method expects a parameter of type obj[]
and the type of the argument is int[]
, so the two types cannot be unified (the key is that the compiler does not automatically convert int[]
to obj[]
). Since this is not possible, it considers the ParamArray
attribute and tries to convert int[]
to obj
and pass it as a member of ParamArray
- this is a conversion that the compiler can automatically perform and so you get the result you described.
If you called Heterogeneous.Echo
with obj[]
as an argument, then it would behave similarly to Generic.Echo
. For example:
Heterogeneous.Echo [| box 0 |]
If you want to go into details, you can look at section 14.4. of the F# language specification. However, the overload resolution rules are pretty complex and so I don't have an exact reference that explains this behaviour - just an informal explanation above.
Upvotes: 5