f# - 在 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> }
Scott Wlaschin 在他的“Domain Modeling made Functional”一书中谈到了使非法状态无法表示,所以我想知道是否有办法对我的复合类型实施某种限制,以便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 使用SafeDate
等一样NonNegativeInt
。
这些仍然可以对其组件属性使用“标准”度量单位。
引用文章:
“度量单位确实可以用来避免混淆不同类型的数值,并且比我们一直使用的单一案例联合强大得多。
另一方面,计量单位没有封装,也没有约束。任何人都可以使用度量单位创建一个 int,并且没有最小值或最大值。”
推荐阅读
- asp.net-core - 如何在 ASP.NET Core 3.1 中禁用 JSON OK 响应(但不是错误)
- python - 递增时间戳
- javascript - onClick 没有触发,事件是一个函数
- flyway - flyway 是否支持基于身份验证密钥的 Snowflake 凭据
- cognos-11 - 从提示值中排除/限制到当前年份 - Cogno BI 11
- wordpress - Woocommerce:订单商品未显示在订单电子邮件中
- android - SweepGradient Android - 如何设置渐变的起始角度
- java - 当我使用 printf 时,它给了我“???”
- javascript - 我可以将 jest 测试作为常规 js 文件运行吗?
- swift - NavigationBar下的SwiftUI空白