Drkcore

20 06 2012 Haskell Tweet

Haskell の printデバッグをWriterTモナドとWriterモナドで

Haskellで躓く理由の一つにPerl,Pythonみたいなノリでprintデバッグしにくいってのがありますね。

Haskell の printf デバッグ / tnomuraのブログでprintデバッグをやっていたので、これをWriterTで書きなおしてみた(最近モナド変換子を理解したので)。

ProductName すごいHaskellたのしく学ぼう!
Miran Lipovača
オーム社 / 2940円 ( 2012-05-23 )


元のコードは階乗計算ですね。

fact 0 = return 1
fact n = do
  print n
  n1 <- fact (n-1)
  return (n * n1)

WriterTモナド変換子を積むとこうなる。

import Control.Monad.Writer

factT :: Int -> WriterT [Int] IO Int
factT 0 = tell [0] >> return 1
factT n = do
  tell [n]
  n1 <- factT (pred n)

tellでログるので、runWriterTで実行する。

*Main> runWriterT $ factT 5
(120,[5,4,3,2,1,0])
*Main> execWriterT $ factT 5
[5,4,3,2,1,0]
*Main> liftM fst $ runWriterT $ factT 5
120

do記法で何故WriterTとIOが剥がれるのかちょっとよくわからなかったんだけど、bindの定義を見たら

instance (Monoid w, Monad m) => Monad (WriterT w m) where
    return a = WriterT $ return (a, mempty)
    m >>= k  = WriterT $ do
        (a, w)  <- runWriterT m
        (b, w') <- runWriterT (k a)
        return (b, w `mappend` w')
    fail msg = WriterT $ fail msg

ってなっていたので、型だけちゃんとあわせてあとはtellをはさめばロギングしてくれるようになっているのねと(便利だ)。

階乗計算の例だったらIOモナドを通さなくてもWriterモナドを使えばピュアな計算にログ機能を付加することもできる。

import Control.Monad.Writer

factW :: Int -> Writer [Int] Int
factW 0 = tell [0] >> return 1
factW n = do
  tell [n]
  n1 <- factW (pred n)
  return $ n * n1

実行する場合はrunWriter

*Main> runWriter $ factW 5
(120,[5,4,3,2,1,0])

こういうのは、理屈がわからなくてもいいからとりあえず身体で覚える的に早い段階で説明されててもいいのかなぁなんて思った。

About

  • もう5年目(wishlistありマス♡)
  • 最近はPythonとDeepLearning
  • 日本酒自粛中
  • ドラムンベースからミニマルまで
  • ポケモンGOゆるめ

Tag

Python Deep Learning javascript chemoinformatics Emacs sake and more...

Ad

© kzfm 2003-2021