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