F# A type parameter is missing a constraint

故事扮演 提交于 2019-12-06 05:57:57

问题


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:

  1. The argument is of type Wrap< ^t> for some fresh ^t.
  2. Therefore x is of type ^t.
  3. Thus, the operands on the right hand side are also both of type Wrap< ^t>.
  4. These values are being passed into the (+) operator. Therefore, Wrap< ^t> needs to support a static operator (+) of type Wrap< ^t> * Wrap< ^t> -> ^u for some fresh type ^u.
  5. The only static operator (+) defined on Wrap<_> has type Wrap< ^a> * Wrap< ^b> -> Wrap< ^c> when (^a or ^b) : (static member (+) : ^a * ^b -> ^c.
  6. Unifying type variables, the overall type of addSelf should be addSelf : 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!