#f#
#f#
Вопрос:
Возможно ли каким-либо образом создать функцию 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, он будет изменять возвращаемый тип.
Есть предложения?
Комментарии:
1. Обратите внимание, что ваш код вычисляет
x^(2^y)
, а неx^y
.
Ответ №1:
Я не думаю, что это возможно. Вы просите функцию вернуть значение <cm^2>
в случае, если мощность равна 2, и <cm^3>
в случае, если значение равно 3 и так далее. Что заставляет функцию возвращать разные «типы» на основе вычисления, что, очевидно, невозможно в статическом типе и языке, безопасном для типов. К сожалению, я не думаю, что единицы измерения можно сделать «универсальными», чтобы попытаться достичь этого дальше.
Ваша функция может иметь только один статический возвращаемый тип.
Комментарии:
1. 1 Ваш ответ правильный — я просто добавил пару деталей, чтобы — надеюсь — прояснить причину.
2. «позволяет функции возвращать разные «типы» на основе вычисления, что, очевидно, невозможно в статическом типе и языке, безопасном для типов». Вы можете сделать это на языках с зависимой типизацией, таких как Idris. И это типобезопасно и статически типизировано.
Ответ №2:
Анкур прав — вы не можете этого сделать (не прибегая к хакерским методам, которые могли бы разбить единицы измерения).
Возможно, более четкое описание проблемы заключается в том, что тип 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
должно было бы отображаться в типе!
Вы можете использовать некоторые неприятные приемы (вдохновленные этой статьей на Хаскелле), но это становится немного безумным. Вместо использования числовых литералов вы бы использовали что-то вроде 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 # snippets, чтобы вы могли видеть предполагаемые типы: http://fssnip.net/4H
Ответ №3:
Как уже было сказано, вы не можете. Если y
не известно во время компиляции, невозможно ввести проверку выражения в системе F # type.
Я подозреваю, что вы будете использовать 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.<_>