2

我正在尝试将一些现有代码重构为更单一的方法。现有代码包含接口IXInterface和数字,例如intbool。默认情况下,数字已经具有Zero,接口将其作为属性获取器,但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

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

失败并显示“类型 bool 不支持运算符 'get_Zero'
失败并显示”类型字符串不支持运算符 'get_Zero'

4

1 回答 1

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# 数学运算符相同,其中任何参数的类型都可以满足静态约束。

这是使这个神奇的源代码的一部分。

目前缺少一个实例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# Core 库中用于泛型数字。

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

于 2015-11-01T19:03:07.430 回答