假设我想创建一个代表可接受的最小/最大界限的 Record 类型:
type Bounds = { Min: float; Max: float }
有没有办法强制 Min < Max?编写一个 validateBounds 函数很容易,我只是想知道是否有更好的方法来做到这一点。
编辑:我意识到对于这个特定的例子,我可能会暴露两个属性并重新排序参数,所以假设我们正在尝试做
type Person = { Name: string }
和 Name 至少需要一个字符。
假设我想创建一个代表可接受的最小/最大界限的 Record 类型:
type Bounds = { Min: float; Max: float }
有没有办法强制 Min < Max?编写一个 validateBounds 函数很容易,我只是想知道是否有更好的方法来做到这一点。
编辑:我意识到对于这个特定的例子,我可能会暴露两个属性并重新排序参数,所以假设我们正在尝试做
type Person = { Name: string }
和 Name 至少需要一个字符。
这是另一个基于保护级别的解决方案:
module MyModule =
type Bounds = private { _min: float; _max: float } with
// define accessors, a bit overhead
member public this.Min = this._min
member public this.Max = this._max
static member public Make(min, max) =
if min > max then raise (ArgumentException("bad values"))
{_min=min; _max=max}
// The following line compiles fine,
// e.g. within your module you can do "unsafe" initialization
let myBadBounds = {_min=10.0; _max=5.0}
open MyModule
let b1 = Bounds.Make(10.0, 20.0) // compiles fine
let b1Min = b1.Min
let b2 = Bounds.Make(10.0, 5.0) // throws an exception
// The following line does not compile: the union cases of the type 'Bounds'
// are not accessible from this code location
let b3 = {_min=10.0; _max=20.0}
// The following line takes the "bad" value from the module
let b4 = MyModule.myBadBounds
我认为您最好的选择是静态成员:
type Bounds = { Min: float; Max: float }
with
static member Create(min: float, max:float) =
if min >= max then
invalidArg "min" "min must be less than max"
{Min=min; Max=max}
并像使用它一样
> Bounds.Create(3.1, 2.1);;
System.ArgumentException: min must be less than max
Parameter name: min
at FSI_0003.Bounds.Create(Double min, Double max) in C:\Users\Stephen\Documents\Visual Studio 2010\Projects\FsOverflow\FsOverflow\Script2.fsx:line 5
at <StartupCode$FSI_0005>.$FSI_0005.main@()
Stopped due to error
> Bounds.Create(1.1, 2.1);;
val it : Bounds = {Min = 1.1;
Max = 2.1;}
但是,正如您所指出的,这种方法的最大缺点是没有什么可以直接阻止构建“无效”记录。如果这是一个主要问题,请考虑使用类类型来保证您的不变量:
type Bounds(min:float, max:float) =
do
if min >= max then
invalidArg "min" "min must be less than max"
with
member __.Min = min
member __.Max = max
为了方便起见,与您从记录中获得的类似的活动模式一起使用(特别是关于模式匹配):
let (|Bounds|) (x:Bounds) =
(x.Min, x.Max)
全部一起:
> let bounds = Bounds(2.3, 1.3);;
System.ArgumentException: min must be less than max
Parameter name: min
at FSI_0002.Bounds..ctor(Double min, Double max) in C:\Users\Stephen\Documents\Visual Studio 2010\Projects\FsOverflow\FsOverflow\Script2.fsx:line 4
at <StartupCode$FSI_0003>.$FSI_0003.main@()
Stopped due to error
> let bounds = Bounds(1.3, 2.3);;
val bounds : Bounds
> let isMatch = match bounds with Bounds(1.3, 2.3) -> "yes!" | _ -> "no";;
val isMatch : string = "yes!"
> let isMatch = match bounds with Bounds(0.3, 2.3) -> "yes!" | _ -> "no";;
val isMatch : string = "no"
字符串示例的一个狡猾的解决方案 - 使用 DU
type cleverstring = |S of char * string
这将强制字符串至少包含一个字符。然后你可以在你的记录中使用cleverstring
而不是string
,尽管你可能想写一些包装函数让它看起来像一个字符串。