是否有可能以某种方式为度量类型创建一个pow函数?
f#中的pow函数只需要int
作为参数,然后Math
类中的pow函数需要float
- 但是剂量允许float<cm>
。
我首先想到:
let rec myPow(x:float<cm>,y:int) =
if y = 0 then x
else myPow(x*x, y - 1)
可能会有用,但显而易见的是,每次遇到else行时都会改变返回类型。
有什么建议吗?
答案 0 :(得分:7)
我认为这是不可能的。您要求函数在电源为2时返回<cm^2>
,在3的情况下返回<cm^3>
,依此类推。这使得函数基于计算返回不同的“类型”,这在静态类型和类型安全语言中显然是不可能的。不幸的是,我认为衡量单位不能成为“仿制药”以试图进一步发展。
您的函数只能有一个静态返回类型。
答案 1 :(得分:6)
Ankur是正确的 - 你不能这样做(不使用破坏单位的黑客)。
对问题的更清楚的描述可能是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 article),但它变得有点疯狂。您可以使用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
答案 2 :(得分:4)
如上所述,你不能。如果在编译时不知道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.<_>