f# - 在 F# 中创建类型的不可比较版本

标签 f# units-of-measurement

我使用 Guid 和字符串作为数据结构中的键。在 C# 中,我花了很多(累积的)时间想知道为什么当 id 是 OrderId 并且我将其与 ContractId 相匹配时,没有事件通过我正在寻找的 id 来实现。我想要做的是防止整个类别的错误。

假设我与以下基础数据类型签订了契约(Contract):

type Contract = { Schedule : Guid; TickTable : Guid; Price : float; Quantity : float }

现在我有两个问题:

let contract =
    { Schedule = Guid.Empty; TickTable = Guid.Empty; Price = 0.; Quantity = 0. }
contract.Schedule = contract.TickTable;; // true - ugh
contract.Price = contract.Quantity;; // true - ugh

我可以解决这样的一个问题:

[<Measure>] type dollars
[<Measure>] type volume
type Contract =
    { Schedule : Guid; TickTable : Guid;
      Price : float<dollars>; Quantity : float<volume> }

现在我们有:

let contract =
    { Schedule = Guid.Empty; TickTable = Guid.Empty;
      Price = 0.<dollars>; Quantity = 0.<volume> }
contract.Schedule = contract.TickTable;; // true - ugh
contract.Price = contract.Quantity;; // type mismatch - yay

有没有办法可以装饰Guids,这样我就会得到类型不匹配的结果?我真的只想影响编译时间 - 理想情况下,编译的代码应该是相同的,如度量单位。

我知道我可以执行以下操作,但它看起来很难看,我希望它会导致运行时影响:

[<Measure>] type dollars
[<Measure>] type volume
type ScheduleId = ScheduleKey of Guid
type TickTableId = TickTableKey of Guid
type Contract =
    { Schedule : ScheduleId; TickTable : TickTableId;
      Price : float<dollars>; Quantity : float<volume> }

let contract =
    { Schedule = ScheduleKey Guid.Empty; TickTable = TickTableKey Guid.Empty;
      Price = 0.<dollars>; Quantity = 0.<volume> }
contract.Schedule = contract.TickTable;; // type error - yay
contract.Price = contract.Quantity;; // type mismatch - yay

最佳答案

您可以将任何类型包装为具有单位,即使是一般情况下,也可以通过使用 [<Measure>] 编写类型来包装任何类型。类型参数。另外,正如拉特金在评论中暗示的那样,使用 struct (which is allocated in place, not as a new object)将节省额外的分配和间接空间。

通用测量单位感知包装器:

type [<Struct>] UnitAware<'T, [<Measure>] 'u> =
    val Raw : 'T
    new (raw) = { Raw = raw }

let withUnit<[<Measure>] 'u> a = UnitAware<_, 'u>(a)

这样,就可以为任意类型提供一个测量单位感知值类型包装器,只需通过 withUnit<myUnit> 进行包装即可并用 .Raw 展开:

let a = 146L |> withUnit<dollars>
let b = 146L |> withUnit<volume>

a = b // Type mismatch.

由于结构比较,两个具有相同单元和相同内容的结构体包装器也将是相等的。与其他计量单位的使用一样,额外的类型安全性在运行时会丢失:box a = box b是真的,就像 box 1.<dollars> = box 1.<volumes> .

关于f# - 在 F# 中创建类型的不可比较版本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27882440/

相关文章:

f# - 无法在 Fsharp 脚本文件中打开命名空间

javascript - JavaScript 的自定义日期单位

php - 使用 php + zend 测量单位的良好数据库模式?

f# - 中断函数的返回值

recursion - F# 递归对象

wcf - 如果通过 WCF 服务返回选项类型的最佳方式会怎样?

algorithm - 如何选择显示值的单位?

android - 获取电池温度和结果单位

ios - 确定系统的距离单位

python - 有没有办法控制 Apache Arrow 批量大小?