F#计量单位能否在OCaml中实施?

时间:2014-02-03 21:50:30

标签: f# ocaml units-of-measurement

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的这个问题,得到了一个有趣的讨论,但没有确定的答案超出'可能不是')。

2 个答案:

答案 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)

首先要创建一组方程式(其中lij的类型为'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) listf : (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章:

http://adam.gundry.co.uk/pub/thesis/thesis-2013-12-03.pdf