问题
I'm trying to define a generic addition operator for a wrapper class. So far I have this: (simplified from the actual code)
type Wrap<'a> =
| Wrap of 'a
static member inline (+) (Wrap x, Wrap y) = Wrap (x + y)
let inline addSelf x = x + x
and indeed it works:
let i = addSelf (Wrap 1) // returns Wrap 2
let f = addSelf (Wrap 1.) // returns Wrap 2.0
but the following alternative to addSelf does not compile
let inline addSelf' (Wrap x) = (Wrap x) + (Wrap x) // compile error
giving error FS0193: A type parameter is missing a constraint 'when ( ^a or ^?15169) : (static member ( + ) : ^a * ^?15169 -> ^?15170)'
Why does the more restricted addSelf' not work when addSelf works fine? Thanks!
回答1:
If you look at the definition of Wrap in FSI, you will see that the static (+) operator is defined only for types that support the restriction, yet Wrap itself is defined for any types.
When write the first version, the type inference adds that restriction for you... but your second version allows any type for x (even for x that do not support the static (+) operator).
I.e. the second version suggest that it is possible to write a function that would support any type of Wrap<'T> although there's no guarantee 'T supports the + operator.
edit: As Tomas pointed out, it's possible but you have to use a special syntax that defines which member to call, the following works:
let inline addSelf (x : Wrap<_>) =
((^a or ^b): (static member (+) : ^a * ^b -> ^c) (x,x))
回答2:
As I said in a comment, I think this is a bug. Here's my reasoning. When the compiler sees
let inline addSelf (Wrap x) = (Wrap x) + (Wrap x)
I think it should make roughly the following inferences:
- The argument is of type
Wrap< ^t>
for some fresh^t
. - Therefore
x
is of type^t
. - Thus, the operands on the right hand side are also both of type
Wrap< ^t>
. - These values are being passed into the
(+)
operator. Therefore,Wrap< ^t>
needs to support a static operator(+)
of typeWrap< ^t> * Wrap< ^t> -> ^u
for some fresh type^u
. - The only static operator
(+)
defined onWrap<_>
has typeWrap< ^a> * Wrap< ^b> -> Wrap< ^c> when (^a or ^b) : (static member (+) : ^a * ^b -> ^c
. - Unifying type variables, the overall type of
addSelf
should beaddSelf : Wrap< ^t> -> Wrap< ^c> when ^t : (static member (+) : ^t * ^t -> ^c)
The various type inference steps are tricky, so it's certainly possible that I'm missing something and this behavior is expected. On the other hand, the various type inference steps are tricky, so they are a bit buggy :). It's also concerning that you can't annotate the function and all subexpressions and get the code to compile:
let inline doStuff< ^t, ^u when ^t : (static member (+) : ^t * ^t -> ^u)> ((Wrap x) : Wrap< ^t>) : Wrap< ^u> =
((Wrap x) : Wrap< ^t>) + ((Wrap x) : Wrap< ^t>)
You still get a compiler error with the mysterious reference to the fresh type parameters ^?12020
and ^?12021
(or whatever the unique ints are in your case). I think this shouldn't happen.
回答3:
I think addSelf'
should still be of the form
let inline addSelf' x = x + x
But with the correct type annotations:
let inline addSelf' (x : Wrap) = x + x
Or something similar to that. I'm a tad rusty on F# and can't test it right now.
回答4:
If you remove inline from the static (+) function i.e
type Wrap<'a> =
| Wrap of 'a
static member (+) (Wrap x, Wrap y) = Wrap (x + y)
Than it works fine.
I guess this is because in the addSelf'
the compiler tries to find the + operator on Wrap type and as the + has been inlined it doesn't match the search criteria.
来源:https://stackoverflow.com/questions/6849557/f-a-type-parameter-is-missing-a-constraint