F # В параметре типа отсутствует ограничение - PullRequest
4 голосов
/ 27 июля 2011

Я пытаюсь определить универсальный оператор сложения для класса-оболочки. Пока у меня есть это: (упрощенно от фактического кода)

type Wrap<'a> =
    | Wrap of 'a
    static member inline (+) (Wrap x, Wrap y) = Wrap (x + y)

let inline addSelf x = x + x

и действительно это работает:

let i = addSelf (Wrap 1)  // returns Wrap 2
let f = addSelf (Wrap 1.) // returns Wrap 2.0

но следующая альтернатива addSelf не компилируется

let inline addSelf'  (Wrap x) = (Wrap x) + (Wrap x) // compile error

выдавая ошибку FS0193: параметру типа не хватает ограничения ', когда (^ a или ^? 15169): (статический член (+): ^ a * ^? 15169 -> ^? 15170)'

Почему более ограниченный addSelf 'не работает, когда addSelf работает нормально? Спасибо!

Ответы [ 4 ]

4 голосов
/ 28 июля 2011

Как я сказал в комментарии, я думаю, что это ошибка.Вот мои рассуждения.Когда компилятор видит

let inline addSelf (Wrap x) = (Wrap x) + (Wrap x)

, я думаю, он должен сделать примерно следующие выводы:

  1. Аргумент имеет тип Wrap< ^t> для некоторых свежих ^t.
  2. Следовательно, x имеет тип ^t.
  3. Таким образом, операнды в правой части также имеют тип Wrap< ^t>.
  4. Эти значения передаются воператор (+).Следовательно, Wrap< ^t> должен поддерживать статический оператор (+) типа Wrap< ^t> * Wrap< ^t> -> ^u для некоторого нового типа ^u.
  5. Единственный статический оператор (+), определенный для Wrap<_>, имеет тип Wrap< ^a> * Wrap< ^b> -> Wrap< ^c> when (^a or ^b) : (static member (+) : ^a * ^b -> ^c.
  6. Объединяя переменные типа, общий тип addSelf должен быть addSelf : Wrap< ^t> -> Wrap< ^c> when ^t : (static member (+) : ^t * ^t -> ^c)

Различные этапы вывода типов являются сложными, поэтому, безусловно, возможно, что я что-то упустили такое поведение ожидается.С другой стороны, различные этапы вывода типов сложны, поэтому они немного ошибочны :).Это также касается того, что вы не можете аннотировать функцию и все подвыражения и получить код для компиляции:

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>)

Вы все еще получаете ошибку компилятора с таинственной ссылкой на свежие параметры типа ^?12020 и ^?12021 (или какие-то уникальные целые в вашем случае).Я думаю, этого не должно быть.

3 голосов
/ 28 июля 2011

Если вы посмотрите на определение Wrap в FSI, вы увидите, что оператор static (+) определен только для типов, поддерживающих ограничение, но сам Wrap определен для любых типов.

Когданаписать первую версию, вывод типа добавляет это ограничение для вас ... но ваша вторая версия допускает любой тип для x (даже для x, которые не поддерживают оператор static (+)).

Т.е. втораяверсия предполагает, что можно написать функцию, которая будет поддерживать любой тип Wrap <'T>, хотя нет никакой гарантии, что T поддерживает оператор +.

edit: как отметил Томас, это возможно, но у вас естьЧтобы использовать специальный синтаксис, который определяет, какой член вызывать, работает следующее:

let inline addSelf (x : Wrap<_>) =
    ((^a or ^b): (static member (+) : ^a * ^b -> ^c) (x,x))
0 голосов
/ 28 июля 2011

Если вы удалите inline из статической (+) функции, т.е.

type Wrap<'a> =
    | Wrap of 'a
    static member (+) (Wrap x, Wrap y) = Wrap (x + y)

Чем все отлично работает.

Полагаю, это связано с тем, что в addSelf' компилятор пытается найти оператор + для типа Wrap, и, поскольку + был встроен, он не соответствует критериям поиска.

0 голосов
/ 27 июля 2011

Я думаю, addSelf' все еще должно иметь форму

let inline addSelf' x = x + x

Но с правильными аннотациями типа:

let inline addSelf' (x : Wrap) = x + x

или что-то подобное.Я немного заржавел на F # и сейчас не могу это проверить.

...