【问题标题】:Can F# units of measure be implemented in OCaml?F# 度量单位可以在 OCaml 中实现吗?
【发布时间】:2014-02-27 14:24:39
【问题描述】:

F# 有一个units of measure capabilityresearch 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 的问题,得到了一个有趣的讨论,但除了“可能不是”之外没有明确的答案。

【问题讨论】:

  • 我对您的建议也是幻像类型,但您是正确的,它们不会按照您的意愿“组合”。您提到的第一种情况(比较错误)和最后一种情况(转换函数)可以由幻像类型处理。
  • 在每种语言中使用它来做任何可以远程物理解释的事情真的很有意义......!
  • 这可以作为类型检查器级别的插件。有一些关于允许类型检查器插件的讨论。在这种情况下,我不知道编译器的当前状态是什么。
  • 为 OCaml 提供编译时度量单位支持似乎是个好主意,顺便说一句,我创建了一个 F# 运行时度量单位库,您可以轻松移植并可能使用它来获得一些灵感:trelford.com/blog/post/Runtime-Units-of-Measure-for-F.aspx
  • 您可以为这些类型编写组合函数(例如,具有 type ('a,'b) pertype 'a measure = { v : float };let (&lt;/&gt;) (a : 'a measure) (b : 'b measure) : (('a,'b) per) measure = {a.v /. b.v} 但您会遇到乘法和关联性问题,我假设 F# 会处理这些问题。

标签: f# ocaml units-of-measurement


【解决方案1】:

快速回答:不,这超出了当前 OCaml 类型推断的能力。

再解释一下:大多数函数式语言中的类型推断都是基于一个名为unification的概念,这实际上只是求解方程的一种特定方法。例如,推断表达式的类型,如

let f l i j =
  (i, j) = List.nth l (i + j)

首先涉及创建一组方程(其中lij的类型分别为'a'b'c,以及List.nth : 'd list -&gt; int -&gt; 'd(=) : 'e -&gt; 'e -&gt; bool、@ 987654331@):

'e ~ 'b * 'c
'a ~ 'd list
'b ~ int
'c ~ int
'd ~ 'e

然后求解这些方程,得到'a ~ (int * int) listf : (int * int) list -&gt; int -&gt; int -&gt; bool。如您所见,这些方程并不难求解;事实上,统一的唯一理论是句法相等,即如果两个事物相等当且仅当它们以相同的方式编写(特别考虑未绑定变量)。

度量单位的问题是生成的方程不能使用句法等式以独特的方式求解;使用的正确理论是阿贝尔群理论(逆、单位元、交换运算)。例如,度量单位m * s * s⁻¹ 应等于m。当涉及到主体类型和 let-generalization 时,还有一个更复杂的问题。例如,以下内容不会在 F# 中进行类型检查:

fun x -> let y z = x / z in (y mass, y time)

因为y 被推断为具有类型float&lt;'_a&gt; -&gt; float&lt;'b * '_a⁻¹&gt;,而不是更一般的类型float&lt;'a&gt; -&gt; float&lt;'b * 'a⁻¹&gt;

无论如何,我建议阅读以下博士论文的第 3 章:

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

【讨论】:

  • 好答案。请注意,稍微轻推一下,F# 将在 let 处概括度量;将注解 :float&lt;_&gt; 添加到 xz 的绑定将导致 y 具有您期望的类型。
【解决方案2】:

它不能直接用类型系统的语法表示,但可以进行一些编码。例如,在 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>

【讨论】:

    猜你喜欢
    • 2011-11-21
    • 2010-11-23
    • 2012-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-12
    • 2021-07-02
    • 2011-02-09
    相关资源
    最近更新 更多