2017-01-17 8 views
4

나는 모나드를 이해하려고 노력하고 있습니다. 나는 분명히 인정해야합니다. 계산식을 작성하고 많은 수의 인쇄 문을 추가 했으므로 누가 호출되는지를 추적 할 수 있습니다.상태 모나드는 바깥 문맥에 어떻게 묶여 있습니까

type State<'st,'a> = 
    | Ok of 'a * 'st 
    | Error of string 
and StateMonadBuilder() = 
    member b.Return(x) = printfn "100 Return %A" x; fun s -> Ok (x, s) 
    member b.ReturnFrom(x) = printfn "100 ReturnFrom %A" x; x 
    member b.Bind(p, rest) = 
     printfn "100 Bind:: %A %A" p rest 
     fun state -> 
      printfn "200 Bind:: %A %A" p rest 
      let result = p state in 
      match result with 
      | Ok (value,state2) -> (rest value) state2 
      | Error msg -> Error msg 

    member b.Get() = 
     printfn "100 Get" 
     fun state -> 
      printfn "200 Get :: %A" state 
      Ok (state, state) 
    member b.Put s = fun state -> Ok ((), s) 

let state = StateMonadBuilder() 

let turn() = 
    state { 
     printfn "100 turn::" 
     let! pos1 = state.Get() 
     printfn "200 turn:: %A" pos1 
     let! pos2 = state.Get() 
     printfn "300 turn:: %A" pos1 
     return! state.Put(fst pos1, snd pos1 - 1) 
    } 

let move() = 
    state { 
     printfn "100 move::" 
     let! x = turn() 
     printfn "200 move:: %A" x 
     let! y = turn() 
     printfn "200 move:: %A" y 
     return x 
    } 

let run() = 
    state { 
     printfn "100 run::" 
     do! move() 
    } 

run() (5,5) |> ignore 

상기 코드는 다음과 같은 출력

100 run:: 
100 move:: 
100 turn:: 
100 Get 
100 Bind:: <fun:[email protected]> <fun:[email protected]> 
100 Bind:: <fun:[email protected]> <fun:[email protected]> 
100 Bind:: <fun:[email protected]> <fun:[email protected]> 
200 Bind:: <fun:[email protected]> <fun:[email protected]> 
200 Bind:: <fun:[email protected]> <fun:[email protected]> 
200 Bind:: <fun:[email protected]> <fun:[email protected]> 
200 Get :: (5, 5) 
200 turn:: (5, 5) 
100 Get 
100 Bind:: <fun:[email protected]> <fun:[email protected]> 
200 Bind:: <fun:[email protected]> <fun:[email protected]> 
200 Get :: (5, 5) 
300 turn:: (5, 5) 
100 ReturnFrom <fun:[email protected]> 
200 move:: <null> 
100 Return <null> 
100 Return <null> 

I이 출력의 제 5 줄 이해를 출력한다. 분명히 run 전화는 move이고 turn 전화는 Get입니다. 그리고 Bind을 호출하는 let! pos1 = ...가 있습니다. 여태까지는 그런대로 잘됐다. 그러나 Bind에 대한 추가 호출이 있습니다. 그들은 어떻게 생겨나나요? 필자는 피상적 인 수준에서 그러한 외부 상황에 대한 바인딩이 어떻게 든 국가 모나드의 마법이어야한다는 것을 이해하지만이 메커니즘은 어떻게 작동합니까? 그리고 turn에 또 다른 let! pos2 = ...이 있는데이 또한 Bind을 트리거하지만 이번에는 이전에 비해 3 회만 한 번 없습니다!

참여 마술, 모든 연기와 거울이 없다 당신의 explainations

+0

생성 된 desugared 코드를 반사경 또는 ILSpy에서 확인해보십시오. – scrwtp

+0

실제로 기술적 인 측면에 대한 질문은 많지 않습니다. 언어 수준에서 그 외부 범위에 액세스하는 방법은 무엇입니까? – robkuz

+0

'<@ state {...} @>를 검토하는 것이 도움이 될 수 있습니다. '당신이 불분명 한 경우에 평가합니다. 추가 정보가 어떤 도움이되는지는 분명하지 않습니다. – kvb

답변

4

을 기대하겠습니다.

워크 플로에서 작성한 계산은 'st -> State<'st, 'a> 유형의 큰 함수 중 하나입니다. run을 호출하는 곳은 사실이 함수를 초기 인수에 적용하는 곳입니다. 즉 바인딩을 통과 한 후 "부모"move 워크 플로우에서 turn으로 전달됩니다. 따라서 중첩 된 워크 플로가 외부에있는 모든 것을 액세스하는 것이 아닙니다. 대신 자신에게 전달합니다.

하나의 비표준 선택은 여러분이하는 일을 이해하기 쉽지 않을 것입니다. 귀하의 상태 모나드는 순수한 상태 모나드가 아니라 오히려 State와 Either/Maybe의 모습을 결합한 것입니다 모나드 (상태 유형의 오류 케이스를 통해). State 타입을 정의하는 동안 실제 모나드 타입은 앞서 언급 한 함수 타입입니다.

일반적인 접근 방식은 다음과 같이 같은 유형을 정의하는 것입니다 :

type State<'st, 'a> = State of ('st ->'a * 'st), 

당신은 함수 유형의 래퍼로서 하나의 경우 조합을 사용하거나 포장 유형없이 기능을 사용하여 즉. 오류 처리는 일반적으로 모나드가 처리하는 것과 관련이 없습니다. do! move(), let! x = turn()let! pos1 = state.Get() - - 이것은 당신이 로그에 표시되는 내용입니다

질문의 두 번째 부분에 관해서는, 당신은 당신의 경로에있는 세 가지 바인딩이 없습니다. 나는 일이 일어나는 순서가 여기서 까다로울 수 있다고 생각한다. 이것이 의미하는 것은 먼저는 다음 Bind를 호출, expr 평가입니다

{| let! pattern = expr in cexpr |} => builder.Bind(expr, (fun pattern -> {| cexpr |})) 

을하고, 계산 cexpr의 마지막 나머지 :

은 바인드 desugared하는 방법을 기억하십시오. 귀하의 경우에, "세 바인딩 깊이"첫 번째 expr을 호출 - Get()에 대한 호출입니다 - 그때 당신은 어떤 시점에서 cexpr의 일부로 다른 바인딩을 호출 바인딩 스택을 해결하기 시작합니다.

let result = p stateBind으로 계산 한 후에 다른 인쇄 명령문을 추가하면 실제 진행 상황을 더 쉽게 볼 수 있습니다.이 상태는 바인드를 풀 때입니다.

+0

"세 가지 바인드"에 대해 좀 더 자세히 설명했습니다. – scrwtp

+0

thx. 나의 가장 큰 정신 매듭은 내가 2 개의 통과가있다라고하는 사실을 들여다 보지 않았다. 컴파일 타임 패스와 런타임 패스 – robkuz

+0

예 - 멋진 중괄호 구문은 컴파일 타임에 코드 아티팩트 일 뿐이며, 서로 중첩 된 일련의 빌더 메소드 호출로 변환됩니다. 컴파일시에는 코드 실행이 전혀 발생하지 않으며, 이것은 순수한 기계적 변형입니다. 그렇기 때문에 모든 빌더는 변환 프로세스에 맞추기 위해 동일한 메소드 이름 및 유형 패턴을 따릅니다. – scrwtp