2016-12-10 6 views
2

私は、ハスケルにとって比較的新しく、Jeremy GibbonsとBruno C.によってThe Essence of the Iterator Patternに示されているように、KernighanとRitchieで実演されたUNIX wcプログラムをモナド構成で移植しようとしています。 S. Oliveira、それをコンパイルするのにいくつかの問題がありました。ここに私のコードだ:GHC 8.0.1 Writer and State monadsのエラー

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インスタンスには少なくとも2つの良い選択肢があります。そのうちの一つは、単位要素として0を持つほか、次のとおりです。

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

、他方は1と乗算である:

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

Haskellの設計者はこれらの2つのオプションを選択する必要がありますどのように?それらはどちらも同じように有効で、両方とも異なる状況で役立ちます。最終的に彼らは自分のMonoidインスタンスを持たずにIntのままにし、代わりに2つの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) 

これら2つのnewtype sがin the Data.Monoid moduleを発見されました。あなたのケースでは、Writerのモナドに数字を追加したいと思っているので、すべてのタイプの署名をWriter IntegerからWriter (Sum Integer)に変更するだけです。


他のタイプのエラーは、GHCはあなたがStateアクションでWriterアクションを構成することができないことを知らせるです。あなたはwcm :: State (Integer, Bool) Stringccm :: Writer Integer Stringです。あなたのコードを読んでスキップすると、状態のIntegerコンポーネントのみを使用しているようです(私はそれがWriterビットと一緒に実行中の合計に参加するつもりだと思います)ので、 Stateモナド変換バージョン使用:

wcm :: StateT Bool (Writer Integer) String 

をしてからStateT富化コンテキストに昔ながらのWriter Integerモナドを持ってliftを使用。

モナド変圧器にまだ慣れていない場合は、State (Integer, Bool)モナドにすべてを書き込むこともできます。

+0

実際、State(Integer、Bool)はおそらくもっと良いでしょう。 'Writer'は' Sum Integer'のようなものではうまく動作しません。 – dfeuer

+0

@dfeuerあなたはそれを定量化できますか?あなたは怠惰を話しますか? –

+0

はい。 「Control.Monad.Writer.Strict」でも「ログ」には怠惰です。独自のMonadWriterインスタンスを実装して使用することもできますが、末尾再帰型への変換は 'State'のようになります。'newtype WriterT 'は、(Functor、Applicative、Monad)を導出するWriterT'(StateT w m a)である。インスタンス(Monoid w、Monad m)=> MonadWriter w(WriterT 'w m)ここでa = WriterT $ modify'(<> a) '。他のすべてのエフェクトがサポートされているかどうかはわかりませんが、おそらくそれはわかりません。結局、あなたは基本的に '国家 'の土地に戻ってきます。いくつかのものがボンネットの下にあり、確認するのが難しいかもしれません。 – dfeuer

関連する問題