F# - 将 LanguagePrimitives.GenericZero 与传递给 class 构造函数的值进行比较
F# - Compare LanguagePrimitives.GenericZero with a value passed on the class contructor
按照此处的建议,,我试图在 class 成员函数中使用 value > LanguagePrimitives.GenericZero
,但我找不到使用方法。
问题可能是值是在类型构造函数中传递的,而不是传递给函数的。
请查看代码注释中的错误。
type IValidationCheck =
abstract member Validate: unit -> Result<unit, string>
type NumberIsPositiveCheck (property:string, value) =
interface IValidationCheck with
member (*inline*) this.Validate () = //# does not allow me to use "inline"
if value > LanguagePrimitives.GenericZero then Ok() //# fail to compile: the type IComparable does not have a get_Zero operator
else Error $"{property} must be greater than zero"
问题是编译器无法确定 value
的类型,它需要确定 LanguagePrimitives.GenericZero
的正确重载。编译器对 value
的唯一了解是您使用 >
运算符对其进行比较,因此它必须实现 IComparable
。这就是 IComparable
出现在错误消息中的原因。
修复它的一种方法是显式指定 value
:
的类型
type NumberIsPositiveCheck (property:string, value: int) =
...
但这会使整个classnon-generic,据我了解,这不是你想要的。
在这里你运气不好:你不能声明一个泛型 class 其中一个类型参数是 SRTP。这是因为底层 .NET 虚拟机根本不支持此类高级类型约束。所以没有办法编码这样的class.
(好吧,完全准确地说,classes 上的 SRTP 实际上受到一些限制,但绝对不存在接口)
但不要绝望,并非一无所有!如果仔细观察 class,您会注意到 Validate()
的结果在对象构造时完全确定。一旦调用构造函数,Validate()
的结果就已经知道了。
因此,您可以做的最简单的事情就是传入布尔结果而不是值:
type NumberIsPositiveCheck (property:string, result) =
interface IValidationCheck with
member this.Validate () =
if result then Ok()
else Error $"{property} must be greater than zero"
let validator = NumberIsPositiveCheck("foo", 42 > 0)
或者您可能想要通过整个 Result<_,_>
,完全切断中间人。
但是为了涵盖我的所有基础,让我们假设出于某种未知原因您确实想要传递值本身,而不是布尔结果。
如果是这样,您还可以将泛型零与值本身一起传递,然后在 Validate()
中进行比较:
type NumberIsPositiveCheck (property:string, value, zero) =
interface IValidationCheck with
member this.Validate () =
if value > zero then Ok()
else Error $"{property} must be greater than zero"
let validator = NumberIsPositiveCheck("foo", 42, 0)
或者,您也可以传递一个比较函数而不是专门为零:
type NumberIsPositiveCheck (property:string, value, compare) =
interface IValidationCheck with
member this.Validate () =
if compare value then Ok()
else Error $"{property} must be greater than zero"
let validator = NumberIsPositiveCheck("foo", 42, fun x -> x > 0)
从这里开始,下一步就很明显了:你为什么要先写一个 class?它的唯一目的似乎是实现接口,你可以完全没有 class:
let inline numberIsPositiveCheck (property:string) value =
{ new IValidationCheck with
member this.Validate () =
if value > LanguagePrimitives.GenericZero then Ok()
else Error $"{property} must be greater than zero"
}
let validator = numberIsPositiveCheck "foo" 42
轰!现在您不必将零作为参数传递,因为现在它是一个函数,而不是 class,因此它可以具有 SRTP。
故事的寓意:classes 更少,功能更多。 类 比函数有用的频率低得多。
按照此处的建议,value > LanguagePrimitives.GenericZero
,但我找不到使用方法。
问题可能是值是在类型构造函数中传递的,而不是传递给函数的。
请查看代码注释中的错误。
type IValidationCheck =
abstract member Validate: unit -> Result<unit, string>
type NumberIsPositiveCheck (property:string, value) =
interface IValidationCheck with
member (*inline*) this.Validate () = //# does not allow me to use "inline"
if value > LanguagePrimitives.GenericZero then Ok() //# fail to compile: the type IComparable does not have a get_Zero operator
else Error $"{property} must be greater than zero"
问题是编译器无法确定 value
的类型,它需要确定 LanguagePrimitives.GenericZero
的正确重载。编译器对 value
的唯一了解是您使用 >
运算符对其进行比较,因此它必须实现 IComparable
。这就是 IComparable
出现在错误消息中的原因。
修复它的一种方法是显式指定 value
:
type NumberIsPositiveCheck (property:string, value: int) =
...
但这会使整个classnon-generic,据我了解,这不是你想要的。
在这里你运气不好:你不能声明一个泛型 class 其中一个类型参数是 SRTP。这是因为底层 .NET 虚拟机根本不支持此类高级类型约束。所以没有办法编码这样的class.
(好吧,完全准确地说,classes 上的 SRTP 实际上受到一些限制,但绝对不存在接口)
但不要绝望,并非一无所有!如果仔细观察 class,您会注意到 Validate()
的结果在对象构造时完全确定。一旦调用构造函数,Validate()
的结果就已经知道了。
因此,您可以做的最简单的事情就是传入布尔结果而不是值:
type NumberIsPositiveCheck (property:string, result) =
interface IValidationCheck with
member this.Validate () =
if result then Ok()
else Error $"{property} must be greater than zero"
let validator = NumberIsPositiveCheck("foo", 42 > 0)
或者您可能想要通过整个 Result<_,_>
,完全切断中间人。
但是为了涵盖我的所有基础,让我们假设出于某种未知原因您确实想要传递值本身,而不是布尔结果。
如果是这样,您还可以将泛型零与值本身一起传递,然后在 Validate()
中进行比较:
type NumberIsPositiveCheck (property:string, value, zero) =
interface IValidationCheck with
member this.Validate () =
if value > zero then Ok()
else Error $"{property} must be greater than zero"
let validator = NumberIsPositiveCheck("foo", 42, 0)
或者,您也可以传递一个比较函数而不是专门为零:
type NumberIsPositiveCheck (property:string, value, compare) =
interface IValidationCheck with
member this.Validate () =
if compare value then Ok()
else Error $"{property} must be greater than zero"
let validator = NumberIsPositiveCheck("foo", 42, fun x -> x > 0)
从这里开始,下一步就很明显了:你为什么要先写一个 class?它的唯一目的似乎是实现接口,你可以完全没有 class:
let inline numberIsPositiveCheck (property:string) value =
{ new IValidationCheck with
member this.Validate () =
if value > LanguagePrimitives.GenericZero then Ok()
else Error $"{property} must be greater than zero"
}
let validator = numberIsPositiveCheck "foo" 42
轰!现在您不必将零作为参数传递,因为现在它是一个函数,而不是 class,因此它可以具有 SRTP。
故事的寓意:classes 更少,功能更多。 类 比函数有用的频率低得多。