Generics 获取或实现String.Zero和bool.Zero以用于幺半群
我正在尝试重构一些现有的代码。现有代码包含接口Generics 获取或实现String.Zero和bool.Zero以用于幺半群,generics,f#,monads,monoids,Generics,F#,Monads,Monoids,我正在尝试重构一些现有的代码。现有代码包含接口IXInterface和数字,如int和bool。默认情况下,数字已经有了Zero,接口将其作为属性gettor,但是bool和string没有。一种解决方法是在接口中封装bool和string,但这很麻烦 我想,如果F#语言能够扩展数字的类型,也许我也可以为我的特殊情况扩展字符串和布尔类型 module MyZero = let inline get_zero () : ^a = ((^a) : (static member get_Zer
IXInterface
和数字,如int
和bool
。默认情况下,数字已经有了Zero
,接口将其作为属性gettor,但是bool
和string
没有。一种解决方法是在接口中封装bool和string,但这很麻烦
我想,如果F#语言能够扩展数字的类型,也许我也可以为我的特殊情况扩展字符串和布尔类型
module MyZero =
let inline get_zero () : ^a = ((^a) : (static member get_Zero : unit -> ^a)())
type System.String with
static member get_Zero() = System.String.Empty
type XR<'T when 'T : (static member get_Zero : unit -> 'T)> =
| Expression of (SomeObj -> 'T)
| Action of (int -> 'T)
| Value of 'T
| Empty
member inline this.Execute(x: SomeObj) : 'T =
match this with
| Value(v) -> v
| Expression(ex) -> ex x
| Action(a) -> a x.GetLocation
| Empty -> get_zero()
static member map f x=
match x with
| XR.Empty -> XR.Empty
| XR.Value v -> XR.Value <| f v
| XR.Action p -> XR.Action <| fun v -> f (p v)
| XR.Expression e -> XR.Expression <| fun x -> f (e x)
// etc
模块MyZero=
让内联get_zero():^a=(^a):(静态成员get_zero:unit->^a)()
键入System.String并使用
静态成员get_Zero()=System.String.Empty
键入XR'T)>=
|(SomeObj->'T)的表达式
|(int->'T)的作用
|'T的价值
|空的
成员内联this.Execute(x:SomeObj):'T=
与此匹配
|值(v)->v
|表达式(ex)->exx
|动作(a)->a x.GetLocation
|清空->获取零()
静态成员映射fx=
将x与
|XR.Empty->XR.Empty
|XR.Value v->XR.Value XR.Action f(PV)
|XR.Expression e->XR.Expression f(ex)
//等
上面的编译很好,只要我不尝试将其用于字符串或布尔值:
type WihtBool = XR<int> // succeeds
type WihtBool = XR<IXInterface> // succeeds
type WihtBool = XR<bool> // fails
type WithString = XR<string> // fails
键入WihtBool=XR//成功
键入WihtBool=XR//成功
键入WihtBool=XR//失败
键入WithString=XR//失败
错误是清楚和正确的(我有一个扩展方法,由于明显的原因没有被识别),我只是不知道一种非侵入性的方法来消除它:
“类型bool不支持运算符‘get_Zero’”因“类型字符串不支持运算符“get_Zero”而失败 F#使用静态优化来扩展数字类型,这是在F#Core库之外禁用的功能 AFAIK获得类似机制的唯一方法是使用重载和静态成员约束 实际上,您正在尝试做的事情已经在中实现了 它的工作原理与F#数学运算符相同,在F#数学运算符中,任何参数的类型都可以满足静态约束 这就是创造这种魔力的部分 当前缺少
bool
的一个实例,但您可以添加一个问题建议它或一个拉请求,它将是一行(或两行)
无论如何,如果要捕获此功能,请尝试以下快速独立代码:
type Mempty =
static member ($) (_:Mempty, _:string) = ""
static member ($) (_:Mempty, _:bool) = false
let inline mempty() :'t = Unchecked.defaultof<Mempty> $ Unchecked.defaultof<'t>
let x:string = mempty()
// val x : string = ""
let y:bool = mempty()
// val y : bool = false
type Boo = Boo with
static member ($) (_:Mempty, _:Boo) = Boo
let z:Boo = mempty()
// val z : Boo = Boo
type Mempty=
静态成员($)(\u:Mempty,\u:string)=“”
静态成员($)(u:Mempty,u:bool)=false
让内联mempty():'t=Unchecked.defaultof$Unchecked.defaultofSo,FSharpPlus作为一个编译库,确保除了核心数字类型之外,还能神奇地为其他类型找到get_Zero
(或者get_One
,op_Multiply
)?然后,对我的类型的约束将不再失败?事实上,它会查找Mempty
成员。成员get_Zero
用于泛型数字,这更有意义,因此它解决了以下问题。如果使用F#+忘记编写约束,它将为您生成。只需使用正确的签名定义成员,在本例中为Mempty()
。如果我正确理解您的编辑,并查看F#+源代码,则未选中。默认值不完全正确,请查看Mempty()
生成的签名。当您使用二元运算符时,静态成员会自动推断,从而使所涉及的两种类型中的任何一种都包含在所求解的约束中。这意味着如果返回类型有重载,它将查找Mempty
type,如果没有重载,它将在返回类型本身中查找该成员。在我的
type Mempty =
static member ($) (_:Mempty, _:string) = ""
static member ($) (_:Mempty, _:bool) = false
let inline mempty() :'t = Unchecked.defaultof<Mempty> $ Unchecked.defaultof<'t>
let x:string = mempty()
// val x : string = ""
let y:bool = mempty()
// val y : bool = false
type Boo = Boo with
static member ($) (_:Mempty, _:Boo) = Boo
let z:Boo = mempty()
// val z : Boo = Boo