2012-09-09 7 views
15

F#の計算式は、構文を持っている:F#計算式に(クラスではなく)ビルダーオブジェクトが必要なのはなぜですか?

identは、ビルダーオブジェクト(この構文は Don Syme's 2007 blog entryから取られている)である
ident { cexpr } 

私が見たすべての例では、ビルダーオブジェクトはシングルトンインスタンスであり、ステートレスで起動します。ドンはattemptと呼ばれるビルダーオブジェクトを定義する例を示します:

let attempt = new AttemptBuilder() 

私の質問:なぜF#は単なる計算式で直接AttemptBuilderクラスを使用していませんか?確かに、この表記法は、インスタンスメソッド呼び出しと同じくらい簡単に静的メソッド呼び出しに取りかかることができます。

インスタンス値を使用するということは、理論的には同じクラスの複数のビルダーオブジェクトをインスタンス化することができることを意味します。おそらく何らかの方法でパラメータ化されています。しかし、それがどのように役立つだろうと私は想像することはできません。


アップデート:私は上に引用構文はビルダーが誤解を招くおそれがあり、おそらく言語の以前のバージョンを反映した単一の識別子として表示される必要があります示唆しています。それは明確な(ビルダーオブジェクトに評価される)任意の式は、構造体の最初の要素として用いることができることになり

expr { comp-or-range-expr } 

:最新F# 2.0 Language Specificationは、などの構文を定義します。

+0

例のようにそれを使用することができます。https: //github.com/mausch/fsharpx/blob/ec5f2de4c1c81aea8ef5e139b10c0cd0091ac2e5/src/FSharpx.Core/Monad.fs#L201 –

答えて

13

あなたの前提は正しいです。ビルダーインスタンスをパラメータ化することができ、その後、パラメータを計算全体を通じて使用することができる。

私はある計算に対する数学的証明の木を構築するためにこのパターンを使用します。各結論は、演算結果、および根本的な結論(見出し語)のN-木問題名のトリプルです。

プルーフツリーを削除して、問題の名前がのの小さな例を示します。より適切と思われるので注釈としましょう。

type AnnotationBuilder(name: string) = 
    // Just ignore an original annotation upon binding 
    member this.Bind<'T> (x, f) = x |> snd |> f 
    member this.Return(a) = name, a 

let annotated name = new AnnotationBuilder(name) 

// Use 
let ultimateAnswer = annotated "Ultimate Question of Life, the Universe, and Everything" { 
    return 42 
} 
let result = annotated "My Favorite number" { 
    // a long computation goes here 
    // and you don't need to carry the annotation throughout the entire computation 
    let! x = ultimateAnswer 
    return x*10 
} 
+0

いい例。ハスケル(そしてOCamlでもそうだと思いますが)は、モナドの型を引数として問題の名前を取る関数と定義することによって同じことが達成されるでしょう。私は試していませんが、F#でも可能です。だから私の元の質問に答えるが、なぜF#のデザイナーが関数連鎖のもっと数学的に純粋なアプローチを強調しないのか不思議に思う。 –

6

これは単なる柔軟性の問題です。はい、Builderクラスが静的であることが要求されていれば簡単になりますが、そのプロセスで多くのことを得ることなく、開発者から柔軟性を取り除くことができます。

たとえば、サーバーと通信するためのワークフローを作成するとします。コードのどこかで、そのサーバーのアドレス(Uri、IPAddressなど)を指定する必要があります。 1つのワークフロー内で複数のサーバーと通信する必要がある/したい場合は、答えが 'none'の場合、さまざまな関数を介して連続して値を渡すのではなく、サーバーのUri/IPAddressを渡すことができるコンストラクタを使用してビルダーオブジェクトを作成する方が理にかなっています。内部的には、ビルダーオブジェクトが値(サーバーのアドレス)をワークフローの各メソッドに適用し、Readerモナのようなものを作成します。

インスタンスベースのBuilderオブジェクトでは、継承を使用して継承された機能を持つBuilderのタイプ階層を作成することもできます。私は誰もこれを実際にやっているのを見たことはありませんが、人々がそれを必要とする場合には柔軟性があります。静的に型付けされたビルダーオブジェクトでは持ちません。

+0

リーダーモナドに感謝します。それは考えて良い例です。 –

3

もう一つの選択肢は、単一のケースを利用することであるが同様に労働組合を区別:

type WorkFlow = WorkFlow with 
    member __.Bind (m,f) = Option.bind f m 
    member __.Return x = Some x 

、あなたが直接パラメータ化計算式の

let x = WorkFlow{ ... } 
関連する問題