F#有一个units of measure capability(此research paper中有更多细节)。
[<Measure>] type unit-name [ = measure ]
这允许定义单位,例如:
type [<Measure>] USD
type [<Measure>] EUR
代码写成:
let dollars = 25.0<USD>
let euros = 25.0<EUR>
// Results in an error as the units differ
if dollars > euros then printfn "Greater!"
它还处理转换(我猜这意味着Measure有一些定义的函数,让Measures成倍增加,分割和取幂):
// Mass, grams.
[<Measure>] type g
// Mass, kilograms.
[<Measure>] type kg
let gramsPerKilogram : float<g kg^-1> = 1000.0<g/kg>
let convertGramsToKilograms (x : float<g>) = x / gramsPerKilogram
这个功能可以在OCaml中实现吗?有人建议我看一下幻像类型,但它们看起来并不像单位一样。
(披露:几个月前我问过关于Haskell的这个问题,得到了一个有趣的讨论,但没有确定的答案超出'可能不是')。
答案 0 :(得分:6)
它不能直接在类型系统的语法中表达,但可以进行某些编码。例如,在caml-list https://sympa.inria.fr/sympa/arc/caml-list/2014-06/msg00069.html上的此消息中已经提出了一个。这是答案的格式化内容。顺便说一句,我看不出为什么这不适用于Haskell的任何理由。
module Unit : sig
type +'a suc
type (+'a, +'b) quantity
val of_float : float -> ('a, 'a) quantity
val metre : ('a, 'a suc) quantity
val mul : ('a, 'b) quantity -> ('b, 'c) quantity -> ('a, 'c) quantity
val add : ('a, 'b) quantity -> ('a, 'b) quantity -> ('a, 'b) quantity
val neg : ('a, 'b) quantity -> ('a, 'b) quantity
val inv : ('a, 'b) quantity -> ('b, 'a) quantity
end = struct
type 'a suc = unit
type ('a, 'b) quantity = float
let of_float x = x
let metre = 1.
let mul x y = x *. y
let add x y = x +. y
let neg x = 0. -. x
let inv x = 1. /. x
end
这成功地追踪了数量的维度:
# open Unit;;
# let m10 = mul (of_float 10.) metre;;
val m10 : ('a, 'a Unit.suc) Unit.quantity = <abstr>
# let sum = add m10 m10;;
val sum : ('a, 'a Unit.suc) Unit.quantity = <abstr>
# let sq = mul m10 m10;;
val sq : ('a, 'a Unit.suc Unit.suc) Unit.quantity = <abstr>
# let cube = mul m10 (mul m10 m10);;
val cube : ('a, 'a Unit.suc Unit.suc Unit.suc) Unit.quantity = <abstr>
# let _ = add (mul sq (inv cube)) (inv m10);;
- : ('a Unit.suc, 'a) Unit.quantity = <abstr>
如果使用不当会出错:
# let _ = add sq cube;;
Characters 15-19:
let _ = add sq cube;;
^^^^
Error: This expression has type
('a, 'a Unit.suc Unit.suc Unit.suc) Unit.quantity
but an expression was expected of type
('a, 'a Unit.suc Unit.suc) Unit.quantity
The type variable 'a occurs inside 'a Unit.suc
# let _ = add m10 (mul m10 m10);;
Characters 16-29:
let _ = add m10 (mul m10 m10);;
^^^^^^^^^^^^^
Error: This expression has type ('a, 'a Unit.suc Unit.suc) Unit.quantity
but an expression was expected of type ('a, 'a Unit.suc)
Unit.quantity
The type variable 'a occurs inside 'a Unit.suc
但是,对于某些事情,它会推断出过于严格的类型:
# let sq x = mul x x;;
val sq : ('a, 'a) Unit.quantity -> ('a, 'a) Unit.quantity = <fun>
答案 1 :(得分:5)
快速回答:不,这超出了当前OCaml类型推断的能力。
更多地解释一下:大多数函数语言中的类型推断都是基于一个名为 unification 的概念,这实际上只是解决方程的一种特定方法。例如,推断表达式的类型,如
let f l i j =
(i, j) = List.nth l (i + j)
首先要创建一组方程式(其中l
,i
和j
的类型为'a
,'b
和'c
分别和List.nth : 'd list -> int -> 'd
,(=) : 'e -> 'e -> bool
,(+) : int -> int -> int
):
'e ~ 'b * 'c
'a ~ 'd list
'b ~ int
'c ~ int
'd ~ 'e
然后求解这些方程式,得到'a ~ (int * int) list
和f : (int * int) list -> int -> int -> bool
。如您所见,这些方程式并不难解决;事实上,统一的唯一理论是语法相等,即如果两个事物相等当且仅当它们以相同的方式编写时(特别考虑未绑定的变量) )。
度量单位的问题在于,使用句法相等性无法以独特的方式求解生成的方程;正确使用的理论是阿贝尔群的理论(逆,身份元素,交换操作)。例如,度量单位m * s * s⁻¹
应相当于m
。当涉及主要类型和let-generalization时,还有一个复杂的问题。例如,以下内容不在F#中进行类型检查:
fun x -> let y z = x / z in (y mass, y time)
因为y
被推断为类型float<'_a> -> float<'b * '_a⁻¹>
,而不是更通用的类型float<'a> -> float<'b * 'a⁻¹>
无论如何,有关更多信息,我建议阅读以下博士论文的第3章: