获取或实现 String.Zero 和 bool.Zero 一般用于幺半群

Getting or implementing String.Zero and bool.Zero generically for use with monoids

我正在尝试重构一些现有代码 into a more monodic approach。现有代码包含接口 IXInterface 和数字 intbool。默认情况下,数字已经有 Zero,接口将其作为 属性 gettor,但 boolstring 没有。一种解决方法是将 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

上面的编译很好,只要我不尝试将它与字符串或布尔一起使用:

type WihtBool = XR<int>         // succeeds
type WihtBool = XR<IXInterface> // succeeds
type WihtBool = XR<bool>        // fails 
type WithString = XR<string>    // fails

错误明确且正确(我有一个扩展方法,由于明显的原因无法识别),我只是不知道摆脱它的非侵入性方法:

fails with "the type bool does not support the operator 'get_Zero'
fails with "the type string does not support the operator 'get_Zero'

F# 设法使用静态优化扩展数字类型,这是在 F# 核心库之外禁用的功能。

据我所知,获得类似机制的唯一方法是使用重载和静态成员约束。

事实上,您正在尝试做的事情已经在 F#+

中实现了
#nowarn "3186"
#r @"FsControl.Core.dll"
#r @"FSharpPlus.dll"

open FSharpPlus

let x:string = mempty()
// val x : string = ""

type Boo = Boo with
    static member Mempty() = Boo

let y:Boo = mempty()
// val y : Boo = Boo

它的工作原理与 F# 数学运算符相同,其中任何参数的类型都可以满足静态约束。

这里是 source code 的一部分,让这一切变得神奇。

目前缺少 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

您可以将 Mempty 重命名为 get_Zero 但我认为 get_Zero 不是幺半群的最佳名称,请记住乘法下的第一个也是一个幺半群并且 get_Zero 已在 F# 核心库中用于通用数字。

但老实说,如果你朝这个方向前进,我强烈建议你考虑那个库,因为在扩展你的代码时你可能会发现很多问题已经解决了,你可以免费获得其他与幺半群相关的函数,比如 mconcatmfold 并且你会在你的类型上获得更好的签名。