1

do には別の動作が必要です。そしてさせてください!私のカスタム計算式で。

私は次の方法でこれを達成しようとします:

type FooBuilder() = class
    member b.Bind<'T, 'U>(x:'T, f:unit->'U):'U = failwith "not implemented" //do! implementation
    member b.Bind<'T, 'U>(x:'T, f:'T->'U):'U = failwith "not implemented" //let! implementation
    member b.Return<'T>(x:'T):'T = failwith "not implemented" //return implementation
end

let foo = FooBuilder()
let x = foo {
    do! ()
    return 2
}

しかし、コンパイラは私にエラーを与えます:

このプログラム ポイントより前の型情報に基づいて、メソッド 'Bind' の一意のオーバーロードを特定できませんでした。利用可能なオーバーロードを以下に示します (または [エラー リスト] ウィンドウに表示します)。型注釈が必要な場合があります。

do! の別の実装を行う方法はありますか? そしてさせて!?

4

2 に答える 2

2

ジェネリックでBind使用される操作を維持したい場合、変換時に F# が異なる実装を使用する必要があると言う方法はありません(オーバーロードは必然的にオーバーラップする必要があります)。let!do!

let!一般に、と で異なる動作を取得したい場合はdo!、計算式がおそらく正しく定義されていないことを示唆しています。この概念は非常に柔軟で、モナドの宣言以外にもさまざまな用途に使用できますが、拡張しすぎている可能性があります。あなたが達成したいことについてもっと情報を書くことができれば、それは役に立ちます。とにかく、ここにいくつかの可能な回避策があります...

ラッピングを追加して、 のように書くことができますdo! wrap <| expr

type Wrapped<'T> = W of 'T
type WrappedDo<'T> = WD of 'T

type FooBuilder() = 
  member b.Bind<'T, 'U>(x:Wrapped<'T>, f:'T->'U):'U = failwith "let!" 
  member b.Bind<'T, 'U>(x:WrappedDo<unit>, f:unit->'U):'U = failwith "do!"
  member b.Return<'T>(x:'T):Wrapped<'T> = failwith "return"

let wrap (W a) = WD a
let bar arg = W arg

let foo = FooBuilder()

// Thanks to the added `wrap` call, this will use the second overload
foo { do! wrap <| bar()
      return 1 }

// But if you forget to add `wrap` then you still get the usual `let!` implementation
foo { do! wrap <| bar()
      return 1 }

もう 1 つの方法は、動的型テストを使用することです。これは少し非効率的です (そして少しエレガントではありません) が、シナリオによってはうまくいくかもしれません:

member b.Bind<'T, 'U>(x:Wrapped<'T>, f:'T->'U):'U = 
  if typeof<'T> = typeof<unit> then 
    failwith "do!" 
  else 
    failwith "let!" 

ただし、これは、書き込み時に引き続きdo!オーバーロードを使用しますlet! () = bar

于 2011-04-06T14:38:22.607 に答える
1

少し醜い他の何かを試すことができますが、うまくいくはずです:

let bindU (x, f) = f x // you must use x, or it'll make the Bind method less generic.
let bindG (x, f) = f x
member b.Bind(x : 'a, f : 'a -> 'b) =
    match box x with
    | :? unit -> bindU (x, f)
    | _ -> bindG (x, f)

a をボックス化し ( に変換obj)、 type かどうかをチェックunitしてから、正しいオーバーロードにリダイレクトします。

于 2011-04-06T14:44:54.557 に答える