Увеличьте тип единицы измерения до определенной мощности - PullRequest
8 голосов
/ 11 мая 2011

Можно ли как-то создать функцию pow для типов мер? Функция pow в f # принимает только int в качестве параметра, а затем функция pow в классе Math принимает float - но отклонение позволяет float<cm>.

Сначала я подумал, что:

let rec myPow(x:float<cm>,y:int) =
    if y = 0 then x
    else myPow(x*x, y - 1)

может сработать, но очевидно, что каждый раз, когда он встречает строку else, он будет менять тип возвращаемого значения.

Есть предложения?

Ответы [ 3 ]

7 голосов
/ 11 мая 2011

Я не думаю, что это возможно.Вы просите функцию вернуть <cm^2> в случае, если мощность равна 2, и <cm^3> в случае 3 и так далее.Что заставляет функцию возвращать различные «типы» на основе вычислений, что, очевидно, невозможно в статическом типе и типобезопасном языке.К сожалению, я не думаю, что единицы измерения можно сделать «обобщенными», чтобы попытаться достичь этого дальше.

Ваша функция может иметь только один статический тип возврата.

6 голосов
/ 11 мая 2011

Анкур прав: вы не можете этого сделать (не прибегая к взломам, которые могут сломать юнитов).

Возможно, более ясное описание проблемы заключается в том, что тип функции pow будет зависеть от значения аргумента, а F # не позволяет вам это сделать.Вы могли бы представить, что это сработало бы, если бы в качестве второго аргумента использовались только литералы, но было бы сложно, если бы вы использовали выражения:

pow a 3 // Assuming a = 1.0<cm>, the return type is float<cm ^ 3>
pow a n // Assuming a = 1.0<cm>, the return type is float<cm ^ n>

Во втором случае значение n должно появиться втип!

Вы можете использовать некоторые неприятные уловки (вдохновленные этой статьей на Haskell ), но это становится немного сумасшедшим.Вместо использования числовых литералов вы должны использовать что-то вроде S(S(S(N))) для представления числа 3.Таким образом, вы можете привести номер в тип.Вы, вероятно, не хотите этого делать, но вот пример:

[<Measure>] type cm

// Represents a number with units of measure powered to the
// number's value (e.g "(S (S O))" has type Num<cm, cm^3>)
type Num<[<Measure>] 'M, [<Measure>] 'N> = 
  | O_ of int * float<'N>
  | S_ of int * Num<'M, 'N / 'M>

// Constructors that hide that simplify the creation  
let O : Num<'M, 'M> = O_ (1, 0.0<_>)
let S n = match n with O_(i, _) | S_(i, _) -> S_(i + 1, n)

// Type-safe power function with units of measure
let pow (x:float<'M>) ((O_(i, _) | S_(i, _)):Num<'M, 'M 'N>) : float<'M 'N> =
  // Unsafe hacky implementation, which is hidden
  // from the user (for simplicity)
  unbox ((float x) ** float i)

let res = pow 2.0<cm> (S (S O))

РЕДАКТИРОВАТЬ: Я отправил исходный код в фрагменты F #, чтобы вы могли видеть предполагаемые типы: http://fssnip.net/4H

4 голосов
/ 11 мая 2011

Как сказано, вы не можете. Если y неизвестно во время компиляции, невозможно проверить тип выражения в системе типов F #.

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

let inline pow2 (x: float<'a>) : float<'a^2> = pown (float x) 2 * 1.<_>
let inline pow3 (x: float<'a>) : float<'a^3> = pown (float x) 3 * 1.<_>
let inline pow4 (x: float<'a>) : float<'a^4> = pown (float x) 4 * 1.<_>
let inline pow5 (x: float<'a>) : float<'a^5> = pown (float x) 5 * 1.<_>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...