【问题标题】:Is it possible to create a discriminated union via a unit of measure tag in F#?是否可以通过 F# 中的度量单位标签创建有区别的联合?
【发布时间】:2014-10-08 01:51:57
【问题描述】:

是否可以通过 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

【问题讨论】:

  • 简单的答案是:没有。
  • 有没有其他方法可以在 F# 中创建自定义后缀运算符?

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


【解决方案1】:

我认为这种特殊组合是不可能的,但如果你愿意,你可以使用智能构造函数:

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:我玩了一点,问题确实是(正如你提供的链接中提到的 - 你只能有一组非常有限的“后缀” - 即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

【讨论】:

  • 这实际上不是我想要的。我看不出写作有多大好处:let var1 = pixel 10 而不是let var1 = Pixel 10。我正在寻找一个像 NumericLiterals 一样的后缀版本,其中可以写 let var1 = 10Z 其中Z 会导致一个 c'tor 调用。
  • 很抱歉,正如我所说:我看不出有任何方法可以按照您想要的方式执行此操作。但这对 cme​​ts 来说太过分了(我试过了,不可读)——要我删除答案吗?
  • PS:我想也许你想要使用计量单位获得的类型安全
  • 不,你的答案很好。只是不是我梦寐以求的银弹解决方案。请不要删除它作为其他人的灵感来源;-)
【解决方案2】:

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

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

如果您只是想颠倒参数的顺序,使数字在单位名称之前,您可以使用管道运算符并编写:

let var1 = 10 |> Pixel
let var2 = Unset

这实质上为您提供了一种编写“后缀运算符”的方法,但使用标准 F# 习惯用法。

【讨论】:

    猜你喜欢
    • 2013-06-27
    • 2018-11-24
    • 1970-01-01
    • 1970-01-01
    • 2018-06-15
    • 2015-06-30
    • 2022-12-02
    • 1970-01-01
    • 2011-10-23
    相关资源
    最近更新 更多