定义f#类型成员中允许的值范围



我正在尝试使用f#中的度量单位,目前我正在尝试创建长度和质量的复合度量单位,以反映英制中的口语,例如"我5英尺10英寸";或者"她重8英石11磅";在美国和英国。

我为标准(非复合)单位定义了一个模块,如下所示:

module Units
// Mass
[<Measure>] type kg // Kilogram
[<Measure>] type g  // Gram
[<Measure>] type lb // Pound (mass)
[<Measure>] type st // Stone (mass)

// Conversions
...
// Length
[<Measure>] type m      // Metre
[<Measure>] type cm     // Centimetre
[<Measure>] type inch   // Inch
[<Measure>] type ft     // Foot
// Conversions
...

我在另一个模块中定义了复合单位:

module CompoundUnits
open Units

// Mass
type StonesAndPounds = {
Stones: float<st>
Pounds: float<lb>
}
// Length
type FeetAndInches = {
Feet: float<ft>
Inches: float<inch>
}
然而,根据我目前编写复合质量和长度类型的方式,存在非法状态(例如负值)和技术上正确的但不首选的状态的空间:
// 39 lbs = 2 st 11 lbs
let eightStoneEleven: StonesAndPounds = { Stones = 6.0<st>; Pounds = 39.0<lb> }
// 22" = 1' 10"
let fiveFootTen: FeetAndInches = { Feet = 4.0<ft>; Inches = 22.0<inch> }

在他的书《Domain Modeling made functional》中;Scott Wlaschin谈到了使非法状态不可表示,所以我想知道是否有一种方法可以对我的复合类型强制执行某种限制,以便0<ft> <= Feet,0<inch> <= Inches <= 12<inch>0<st> <= Stones,0<lb> <= Pounds <= 14<lb>

常见的模式是为该类型创建一个模块,该模块包含其定义以及create函数和其他验证逻辑。

Scott在他的网站上有一些例子,作为他的"设计类型"系列的一部分。

https://fsharpforfunandprofit.com/posts/designing-with-types-non-strings/

您不能对度量单位本身强制限制,但是您可以创建专用类型来表示您的复合度量,就像Scott对SafeDateNonNegativeInt等所做的那样。

它们仍然可以使用"标准"度量单位来衡量它们的组件属性。

引自文章:

"度量单位确实可以用来避免混淆不同类型的数值,并且比我们一直使用的单例联合要强大得多。

另一方面,度量单位没有封装,不能有约束。任何人都可以用度量单位创建int,没有最小值或最大值。">

最新更新