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