获取或实现String.Zero和bool.Zero通常用于monoids

时间:2015-11-01 18:45:10

标签: generics f# monads monoids

我正在尝试重构一些现有代码into a more monodic approach。现有代码包含接口b*-和数字IXInterfaceint。默认情况下,数字已经bool,接口将其作为属性gettor,但Zerobool没有。一种方法是在界面中包含bool和string,但这很麻烦。

我认为如果F#语言设法扩展数字的类型,也许我也可以为字符串和bool做我喜欢的特定情况。

string

以上编译很好,只要我不尝试使用字符串或bools:

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

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

  

失败,“bool类型不支持运营商'get_Zero'   失败,“类型字符串不支持运算符'get_Zero'

1 个答案:

答案 0 :(得分:5)

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

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

你确实想要做的事情已经在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不是幺半群的最佳名称,请记住乘法下的第一个也是幺半群{{1}已经在F#核心库中用于通用数字。

但老实说,如果你正朝着这个方向前进,我强烈建议你考虑一下这个库,因为在扩展已经解决的代码时你会发现很多问题,你可以免费获得其他类似monoid的函数,比如{{1}和get_Zero,你的类型会得到更好的签名。