使类型别名在 F# 中不可互换

making type aliases non interchangeable in F#

这是一个例子:

type T =
    TimeSpan
    
let x (y: T) =
    printfn $"{y}"

let a:TimeSpan = TimeSpan.FromSeconds(3)
let b:T        = TimeSpan.FromSeconds(3)

let a' = x a
let b' = x b

在这种情况下,我想创建一个与 TimeSpan 相同但不是 TimeSpan 的类型。我希望能够相互转换,但在函数签名方面并不等同。

例如,x 函数采用类型 T,所以行:

let a' = x a

不应编译,因为它传递了 TimeSpan

但是行:

let b' = x b

正在传递类型 T 并且应该编译。

是否有一些简单而优雅的方法可以实现这一点?这样做的目标是能够使用 TimeSpan,但将选项限制为某些特定值。

现在我正在使用需要转换为 TimeSpan 的枚举,然后如果 TimeSpans 存在于枚举中,则将其重新转换为枚举。比较丑

I want to make a type which is identical to TimeSpan but is NOT TimeSpan. I would like the ability to cast between one another, but not be equivalent when it comes to function signatures.

这在 F# 中是不可能的,但完成类似事情的通常方法是使用 single-case unions,例如:

type T = T of TimeSpan

let x (T y) =
    printfn "%A" y   // y is the inner TimeSpan value

let a:TimeSpan = TimeSpan.FromSeconds(3.0)
let b:T        = TimeSpan.FromSeconds(3.0) |> T

let a' = x a   // doesn't compile
let b' = x b

我认为您在这里处理的一般问题称为“primitive obsession”。

您可以使用单格 DU,也可以使用计量单位来执行此操作。 UMX 特别支持时间跨度。

https://github.com/fsprojects/FSharp.UMX