2

既存のコードをよりモノディックなアプローチにリファクタリングしようとしています。既存のコードには、 や などのインターフェイスと数値が含まれていIXInterfaceます。数値はデフォルトですでに持っており、インターフェイスにはプロパティ gettor としてそれがありますが、そうではありません。1 つの方法は bool と string をインターフェイスでラップすることですが、これは面倒です。intboolZeroboolstring

私は、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# コア ライブラリ以外では無効になっている機能である静的最適化を使用して、数値型を拡張しています。

私の知る限り、同様のメカニズムを取得する唯一の方法は、オーバーロードと静的メンバー制約を使用することです。

実際、あなたがやろうとしていることは、すでに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ありませんが、それを示唆するイシューまたはプル リクエストを追加できます。これは 1 行 (または 2 行) になります。

とにかく、この機能をキャプチャしたい場合は、次の簡単なスタンドアロン コードを試してください。

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 もモノイドでありget_Zero、F# コア ライブラリで一般的な数として既に使用されていることを思い出してください。

mconcatしかし、正直なところ、この方向に進んでいる場合は、そのライブラリを検討することを強くお勧めします。コードをスケーリングするときに見つかる可能性のある多くの問題が既に解決されているためですmfold。あなたのタイプ。

于 2015-11-01T19:03:07.430 に答える