4

これは、この質問に対するフォローアップの質問です。

カスタム操作によって値を蓄積し、同時に標準の F# 言語構造もサポートする計算式ビルダーを作成しようとしています。簡単な例を説明するために、F# リストを作成する計算式を使用しています。kvb と Daniel からの提案のおかげで、私はさらに進んでいますが、まだforループに問題があります。

ビルダー:

type Items<'a> = Items of 'a list

type ListBuilder() =
    member x.Yield(vars) = Items [], vars
    member x.Run(l,_) = l
    member x.Zero() = Items [], ()
    member x.Delay f = f()
    member x.ReturnFrom f = f

    member x.Combine((Items curLeft, _), (Items curRight, vars)) =
        (Items (curLeft @ curRight), vars)

    member x.Bind(m: Items<'a> * 'v, f: 'v -> Items<'a> * 'o) : Items<'a> * 'o =
        let (Items current, vals) = m
        x.Combine(m, f vals)

    member x.While(guard, body) =
        if not (guard()) then
            x.Zero()
        else
            x.Bind(body, fun () -> x.While(guard, body))

    member x.TryWith(body, handler) =
        try
            x.ReturnFrom(body())
        with e ->
            handler e

    member x.TryFinally(body, compensation) =
        try
            x.ReturnFrom(body())
        finally
            compensation()

    member x.Using(disposable:#System.IDisposable, body) =
        let body' = fun() -> body disposable
        x.TryFinally(body', fun () ->
            match disposable with
            | null -> ()
            | disp -> disp.Dispose())

    member x.For(xs:seq<'a>, body) =
        x.Using(xs.GetEnumerator(), fun enum ->
            x.While(enum.MoveNext, x.Delay(fun () -> body enum.Current)))

    [<CustomOperation("add", MaintainsVariableSpace=true)>]
    member x.Add((Items current, vars), [<ProjectionParameter>] f) =
        Items (current @ [f vars]), vars

    [<CustomOperation("addMany", MaintainsVariableSpace=true)>]
    member x.AddMany((Items current, vars), [<ProjectionParameter>] f) =
        Items (current @ f vars), vars


let listBuilder = ListBuilder()

let build (Items items) = items

このバージョンでは、以前はできなかった次のようなことが可能になります。

let stuff = 
    listBuilder {
        let x = 5 * 47
        printfn "hey"
        add x
        addMany [x .. x + 10]
    } |> build

ただし、これについてはまだコンパイラ エラーが発生します。

let stuff2 =
    listBuilder {
        for x in 1 .. 50 do
            add x            
    } |> build

この場合、IDE は x in に下線for x inを付けて、「この式は unit 型を持つことが期待されていましたが、ここでは int 型です」と伝えています。

ループ変数がユニット型であると期待している理由はよくわかりません。明らかに、どこかで間違ったメソッド シグネチャを持っています。蓄積された状態をすべての場所で通過していないのではないかと思いますが、コンパイラ エラーは、間違った場所を絞り込むのに役立っていません。任意の提案をいただければ幸いです。

4

1 に答える 1

3

直接の原因は、While関数が の型を制約していることですbody。ただし、一般に、同じ計算式でカスタム操作と制御フロー演算子の両方を使用することはできないため、署名を修正しても、思い通りに実行できるとは思えません。

于 2014-04-18T20:24:46.323 に答える