2016-12-13 1 views
4

Некоторые способы использования не компилируются, когда одна сторона оператора имеет известный тип, а другой - нет. Одним из примеров является с единицами измерения:Почему автоматическое обобщение иногда выдает чрезмерно конкретные типы?

let inline multiplyWithFive x = 5. * x 

type [<Measure>] myUnit 
let test = multiplyWithFive 3.<myUnit> // Compiler error 

5. * 3.<myUnit> явно допустимое выражение, так что это удивительно, особенно если учесть, что inline функции обобщать максимально в других случаях:

let inline multiply a b = a * b 
let test = multiply 5. 3.<myUnit> // Valid 

Это не ограничено к единицам измерения. Скажем, я делаю тип, который поддерживает асимметричное умножение с помощью float. Это несовместимо с функцией multiplyWithFive, которая произвольно выводит его параметр как float:

type BoxFloat = 
    { Value : float } 

    static member inline (*) (lhs : float, rhs : BoxFloat) = 
     { Value = lhs * rhs.Value } 

let boxThree = { Value = 3. } 

let test2 = multiplyWithFive boxThree // Compiler error 

Опять 5. * boxThree является допустимым выражением. Но автоматическое обобщение, похоже, не признает этого.

Я могу «исправить» первый случай с аннотациями типа единицы измерения, но это ограничивает базовый тип без видимых причин. Если мне действительно нужна более общая функция, я не знаю, как остановить компилятор от ограничений. Если я явно укажу общий параметр, он просто отказывается сохранить его общий:

// Warning: This construct causes code to be less generic than indicated... 
let inline multiplyWithFive (x : 'T) = 5. * x 

Что я могу сделать с этим? Есть ли способ сказать, что мне нужна более общая версия?

ответ

5

Чтобы ответить на ваш первый вопрос, я думаю, что компилятор F # не будет автоматически обобщать общий тип, чтобы включить возможные единицы, поэтому ваш первый пример заканчивается тем, что принимает float. Он получает обобщенное над прибором, если вы укажете поплавок принимает блок в аннотации типа:

let inline multiplyWithFive (x:float<_>) = 5. * x 

type [<Measure>] myUnit 
multiplyWithFive 3.   // Works fine without units 
multiplyWithFive 3.<myUnit> // Works fine with units 

Что касается создания этой работы с любым типом, который поддерживает оператор умножения - Я думаю, что F # компилятор специальный корпус оператор умножения так что он дает разумное поведение по умолчанию в типичном случае, когда вы действительно хотите работать с float или float<_> значениями. Если определить это с помощью явного ограничения члена, предупреждение довольно четкое представление о том, что:

// warning FS0077: Member constraints with the name 'op_Multiply' 
// are given special status by the F# compiler 
let inline amultiplyWithFive (x:^T) : ^R = 
    (^T : (static member (*) : float * ^T -> ^R) (5.0, x)) 

// no warning since we're requiring operator ** 
let inline multiplyWithFive (x:^T) : ^R = 
    (^T : (static member (**) : float * ^T -> ^R) (5.0, x)) 

Вы, вероятно, может обойти это, используя статические ограничения членов в более причудливым образом - есть trick that lets you define overloaded operators с использованием статических memebrs. Тем не менее, я думаю, что это немного растягивает механизм, и у него, вероятно, будут последствия юзабилити в других частях вашего кода.

+0

Oooh! Это особый случай для некоторых примитивных типов и операторов! Поведение отличается, если я использую собственный тип вместо float! Я думаю, что первая часть этого ответа является частным случаем второй части. Как видно из «let inline multiply a b = a * b», компилятор не обязательно умаляет обобщение по единицам измерения. И при задании 'type M <[] 'u>() = class end',' let inline multiplyWithM x = M () * x' выводится максимально общим! – Vandroiy

Смежные вопросы