是否可以通过F#中的度量单位标记创建区分联合?

时间:2014-08-14 12:02:03

标签: f# units-of-measurement discriminated-union

是否可以通过F#中的测量单位标签创建区分联合类型?

我想写...如下:

type DomainObject =
| Pixel           of int
| ScaledPixel     of int
| Centimeter      of float
| Unset

let var1 = 10<px> // should equal: let var1 = Pixel(10)
let var2 = 0<us>  // should equal: let var2 = Unset

let process sth =
  match sth with
  | Pixel(p) -> ...
  | Centimeter(c) -> ...
  // etc.

使用NumericLiterals这样的事情是可能的。但是,人们只能使用少量的文字,如Neil P. showed

2 个答案:

答案 0 :(得分:5)

正如我在评论中所说,简单的答案是否定的。

在某种程度上,您试图滥用一个F#功能(度量单位)来模拟其他语言(后缀运算符)中可能存在的功能,这可能是一件坏事,因为(即使有可能),结果代码也会很混乱。

如果您只想反转参数的顺序以使数字位于单位名称之前,则可以使用管道运算符并写入:

let var1 = 10 |> Pixel
let var2 = Unset

这实际上为您提供了一种编写&#34;后缀运算符&#34;的方法,但使用标准F#习语。

答案 1 :(得分:3)

我不认为这种特殊组合是可行的,但如果您愿意,可以选择智能构造函数:

module Domain =

    [<Measure>] type px
    [<Measure>] type spx
    [<Measure>] type cm
    // ...


    type DomainObject =
    | Pixel           of float<px>
    | ScaledPixel     of float<spx>
    | Centimeter      of float<cm>
    | Unset

    let inline pixel f = Pixel <| float f * 1.0<px>
    let inline scaledPixel f = ScaledPixel <| float f * 1.0<spx>
    let unset = Unset
    // ...

    let var1 = pixel 10
    let var2 = unset

    let process sth =
      match sth with
      | Pixel(p) -> ...
      | Centimeter(c) -> ...
      // etc.

我认为这是合理的关闭 - 如果你想要你可以使构造函数私有并添加活动模式(重新启用模式匹配)或访问器来完全封装实现细节。

如果您喜欢,您甚至可以添加(+)(-),...

PS:内联是让函数使用各种数值;)

PPS:我玩了一下,问题确实存在(正如你给出的链接中提到的那样 - 你只能拥有一组非常有限的&#34;后缀&#34; - 即Q, R, Z, I, N, and G) - 例如,这种工作:

module NumericLiteralQ =
  open Domain

  let inline FromZero() = Pixel 0.0<px>
  let inline FromOne() = Pixel 1.0<px>
  let inline FromString (s:string) =
      System.Double.Parse s * 1.0<px> |> Pixel
  let inline FromInt32 (n:int) =
      1.0<px> * float n |> Pixel
  let inline FromInt64 (n:int64) = 
      1.0<px> * float n |> Pixel

但是我认为编写

非常容易
let p = 5Q

而不是

let p = pixel 5

let p = 5 |> pixel