2016-12-10 5 views
2

Jeremy Gibbons 및 Bruno C.의 The Essence of the Iterator Pattern에 나와있는 것처럼 하스켈과 Kernighan 및 Ritchie에서 모나드 구성을 사용하여 시연 된 UNIX wc 프로그램을 포팅하려고 비교적 처음입니다. S. Oliveira, 컴파일하는데 어려움을 겪고 있습니다.GHC 8.0.1 오류 (작성자 및 주립 모 더드)

import Control.Monad.Writer 
import Control.Monad.State 
import Data.Char 

test :: Bool -> Integer 
test b = if b then 1 else 0 

ccmBody :: Char -> Writer Integer Char 
ccmBody c = do 
    tell 1 
    return c 

ccm :: String -> Writer Integer String 
ccm = mapM ccmBody 

lcmBody :: Char -> Writer Integer Char 
lcmBody c = do 
    tell(test(c == '\n')) 
    return c 

lcm' :: String -> Writer Integer String 
lcm' = mapM lcmBody 

wcmBody :: Char -> State (Integer, Bool) Char 
wcmBody c = let s = not (isSpace c) in do 
       (n,w) <- get 
       put (n + test(not (w || s)), s) 
       return c 

wcm :: String -> State (Integer, Bool) String 
wcm = mapM wcmBody 

clwcm = ccm >=> lcm' >=> wcm 

그리고 컴파일러 오류 :

wordcount.hs:10:3: error: … 
    • No instance for (Monoid Integer) arising from a do statement 
    • In a stmt of a 'do' block: tell 1 
     In the expression: 
     do { tell 1; 
      return c } 
     In an equation for ‘ccmBody’: 
      ccmBody c 
      = do { tell 1; 
        return c } 
wordcount.hs:33:26: error: … 
    • Couldn't match type ‘StateT 
          (Integer, Bool) Data.Functor.Identity.Identity’ 
        with ‘WriterT Integer Data.Functor.Identity.Identity’ 
     Expected type: String 
        -> WriterT Integer Data.Functor.Identity.Identity String 
     Actual type: String -> State (Integer, Bool) String 
    • In the second argument of ‘(>=>)’, namely ‘wcm’ 
     In the second argument of ‘(>=>)’, namely ‘lcm' >=> wcm’ 
     In the expression: ccm >=> lcm' >=> wcm 
Compilation failed. 

나는 내가 잘못 여기 두 경우 모두에서 뭘하는지 이해할 수 없다 여기 내 코드입니다. 어떤 도움이라도 대단히 감사하겠습니다. 감사!

답변

3

Writer 모나드가 작동하려면 작성중인 객체가 Monoid의 인스턴스 여야합니다. tellmappend을 사용하여 실행중인 로그에 인수를 추가합니다.

그러나 정수의 경우 Monoid 인스턴스에는 적어도 두 가지 좋은 선택이 있습니다. 그 중 하나는 단위 요소로 0, 추가 내용입니다

instance Monoid Int where 
    mempty = 0 
    mappend = (+) 

다른 하나는 1 곱셈이다 :

instance Monoid Int where 
    mempty = 1 
    mappend = (*) 

어떻게 하스켈 디자이너는이 두 가지 옵션 중 하나를 선택해야합니까? 둘 다 똑같이 유효하며 둘 다 다른 상황에서 유용합니다. 결국 그들은 자신의 인스턴스가 Monoid이 아닌 Int으로 남겨두고 원하는 인스턴스를 선택할 수 있도록 두 개의 newtype을 제공하기 위해 의견을 표명하지 않기로 결정했습니다.

newtype Sum n = Sum { getSum :: n } 
instance Num n => Monoid (Sum n) where 
    mempty = Sum 0 
    Sum x `mappend` Sum y = Sum (x + y) 

newtype Product n = Product { getProduct :: n } 
instance Num n => Monoid (Product n) where 
    mempty = Product 1 
    Product x `mappend` Product y = Product (x * y) 

이 두 newtype

in the Data.Monoid module를 찾을 수 있습니다. 귀하의 경우에 Writer 모나드에 숫자를 더하고 싶습니다. 따라서 모든 유형 서명을 Writer Integer에서 Writer (Sum Integer)으로 변경해야합니다.

다른 유형의 오류

는 GHC 당신이 State 행동으로 Writer 조치를 구성 할 수 있음을 말하고있다. wcm :: State (Integer, Bool) Stringccm :: Writer Integer String이 있습니다. 코드를 읽는 것만으로도 상태를 추가하기 위해 상태의 Integer 구성 요소를 사용하는 것 같습니다 ( Writer 비트와 함께 누적 합계에 참여할 의향이 있습니까?)

wcm :: StateT Bool (Writer Integer) String 

을 다음 StateT 풍성하게 상황에 평범한 구식 Writer Integer 모나드을 가지고 lift를 사용하여 : State모나드 변압기 버전을 사용.

아직 모나드 트랜스포머에 익숙하지 않은 경우 State (Integer, Bool) 모나드에 모든 내용을 쓰는 것이 좋습니다.

+0

실제로 State (Integer, Bool)는 아마도 더 좋을 것입니다. 'Writer'는'Sum Integer'와 같이 잘 작동하지 않습니다. – dfeuer

+0

@ dfeuer 계량화 해 주시겠습니까? 게으름에 대해 이야기하고 있습니까? –

+0

예. 심지어 'Control.Monad.Writer.Strict'는 "로그"에서 게으르다. 자신 만의 MonadWriter 인스턴스를 구현하여 사용할 수는 있지만 tail-recursive 형식으로의 변환은 결국'State'처럼 보일 것입니다.'newtype WriterT '= WriterT'(StateT wm a) 파생 (Functor, Applicative, Monad); 인스턴스 (Monoid w, Monad m) => MonadWriter w (WriterT 'w m) 여기서 a = WriterT $ modify'(<> a)'. 다른 모든 효과가 지원되는지는 잘 모르겠지만 아마도. 결국, 당신은 기본적으로'State' 땅으로 되돌아 간다. 확인하기 위해 까다로워 질 수있는 약간의 것들이있다. – dfeuer