2014-05-10 8 views
1

나는 그것이 작동 할 수있는 모든 다른 데이터 형식에 대해 많은 경우와 함께 길고 재귀적인 기능을 가지고 있습니다. 함수의 반환 형식은 Maybe이며 Nothing은 실패를 나타냅니다. 재귀 호출은 하위 호출 중 하나라도 실패하면 실패하므로 함수의 각 경우에 대한 계산은 do 블록에서 수행됩니다.재귀 모나 딕 코드에서 모나드를 추적 오류로 변환

필자가 지금까지 가지고있는 구현은 분명히 버그가 있습니다. 왜냐하면 필자는 입력을 받아야하고 실패한 것입니다. 즉, Nothing으로 평가됩니다. 그러나 어느 subcall이 Nothing으로 평가되는지 확인할 수 없습니다. 이런 일이 일어나는 곳을 찾아 내기 위해 모든 재귀 호출을 추적하고 싶습니다. 나는 traceMaybebind (즉, (>>=))으로 삽입 할 수 있다면, 함수의 모든 경우에 trace을 삽입 할 필요가 없다는 생각이 들었다.

난 그냥 그 Monad 예와 함께 Maybe 유형의 구현을 복사하여 내가 설명을 수정,이 아이디어를 구현하려면 : 나는 bind 함수에 trace에 전화를 삽입했다. I (예를 들어, catMaybes) 내 코드는 Monad 실제로 Maybe 것을 가정 몇 가지를 변경했다하더라도
data TraceMaybe a = TNothing | TJust a 
    deriving (Eq, Ord, Show) 

tracet :: TraceMaybe a -> TraceMaybe a 
tracet TNothing = trace "monad failed" TNothing 
tracet x  = trace "monad good" x 

instance Monad TraceMaybe where 
    (TJust x) >>= k = tracet $ k x 
    TNothing >>= _ = TNothing 
    (TJust _) >> k = tracet k 
    TNothing >> _ = TNothing 
    return   = TJust 
    fail m   = TNothing 

는 그럼 난, 그냥 큰 함수의 반환 형식을 변경하는 대부분 수 있었다

제 질문은 이것입니다 : Maybe의 약간 수정 된 버전을 만드는 더 우아하고 구성적인 방법이 있습니까? 나는 "Monad 변압기"에 대해 들었지만, 본 적이있는 예는 완전히 관련이없는 것 같습니다.

글쓴이와 어쩌면 이것을 결합하는 방법이 있는지 궁금합니다.

답변

2

예, WriterMaybe을 결합하여 문제를 해결할 수 있습니다.

모나드 트랜스포머의 경우 주문이 중요합니다. 기본 모나드가되어야하고 변압기는 무엇입니까? 여기서 Writer이 기본 모나드 여야합니다. 그렇지 않으면 실패로 인해 로그가 지워지기 때문입니다 (this other answer 참조).

import Control.Monad 
import Control.Monad.Writer.Strict 
import Control.Monad.Trans.Maybe 

computation :: Int -> MaybeT (Writer [String]) Int 
computation i = do 
    foo <- return 5 
    lift $ tell ["This is a log message"] 
    if i == 3 
     then mzero -- from MonadPlus, of which MaybeT is an instance 
     else return $ i + foo 

*Main> runWriter . runMaybeT $ computation 3 
(Nothing,["This is a log message"]) 

*Main> runWriter . runMaybeT $ computation 2 
(Just 7,["This is a log message"]) 

변압기에 대한 좋은 소개는 "Monad Transformers Step by Step"입니다.

mtl 패키지는 많은 경우에 lift들과 코드를 뿌리는 피하도록하고, 또한 대신 정확한 모나드 스택을 지정할 필요없이 "모나드는 MonadWriter 기능을 가져야한다"말보다 일반적인 서명을 (쓸 수 typeclasses있다).

computation' :: (MonadWriter [String] m) => Int -> MaybeT m Int 
computation' i = do 
    foo <- return 5 
    tell ["This is a log message"] -- no explicit lift 
    if i == 3 
     then mzero  
     else return $ i + foo 

로그 메시지의 수가 APPEND 함수 고가이므로 Writer 의해 모노 이드 사용자가 비효율적으로 목록을 사용하여, 성장

. dlists과 같이 효율적으로 추가 할 수 있습니다.

가끔 Writer끝의 로그 메시지에만 액세스 할 수 있기 때문에 불편합니다. 계산 중입니다.conduit 또는 pipes 패키지의 것과 같은 스트리밍 모나드 변환기를 사용하면 모나드를 IO에 살지 않고도 계산 중간에 메시지를 기록 할 수 있습니다.

+3

'if cond then mzero else ...'는'guard (not cond)와 동일합니다. ... '. – Xeo