2011-04-06 2 views
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 
} 

을하지만 컴파일러는 나에게 오류를 제공합니다 :

나는 다음과 같은 방법으로이를 달성하려고

A unique overload for method 'Bind' could not be determined based on type information prior to this program point. The available overloads are shown below (or in the Error List window). A type annotation may be needed.

할의 다른 구현을 할 수있는 방법이 있나요! 그리고하자!?

답변

2

let!에서 사용되는 Bind 연산을 유지하려면 do!을 변환 할 때 F #이 다른 구현을 사용해야한다고 말하는 방법이 없습니다 (오버로드가 반드시 중복되어야 함).

일반적으로 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 } 

다른 대안은 동적 유형 테스트를 사용하는 것입니다. 이것은 비효율적 조금 (그리고 우아 비트)이지만, 시나리오에 따라, 트릭을 할 수 있습니다 : 당신이 let!() = bar을 쓸 때

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

그러나,이 여전히 do! 오버로드를 사용합니다.

+0

매우 자세한 설명을 주셔서 감사합니다. –

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로 변환) 상자와 검사를 올바른 과부하로 리디렉션를 입력 한 다음 unit 인 경우.

+0

http://ramon.org.il/wp/2011/04/taking-computation-expressions-one-step-further/을 참조하십시오. 그것은 당신에게 바인딩 키워드와 함께 좀 더 자유를 제공합니다. –

+0

이 해결 방법을 보내 주셔서 감사드립니다. –

관련 문제