Tuesday 24 May 2011

Haskell Monad

授業で使うので復習中。

http://www.sampou.org/haskell/tutorial-j/io.html

を見ると、

  getll = do
    c <-getChar
    if c=='\n'
      then return []
      else do
         l <- getll
         return (c:l)

これが、Hasklell の getline 。(c:l) はリストのcons ね。動くんですが、

  indent を間違えると動きません

getll = do c <-getChar と書くとアウトらしい。しかも、これは、Haskell の version 依存があるみたい。まぁ、それは許すんだが...

  Pureな関数型言語なのに getChar なんて許されるのかよ

ってのは、そうなんだけど、それが許されるのが Monad 。全然、関数型言語のプログラムに見えないところが良いらしい。

Prelude> :t getChar
getChar :: IO Char

ということなので、Haskell の top level が IO を補っているらしいです。

  getChar >>= putChar

とかも可能。

  getChar >>= \x -> return x

と lambda 式で受けることもできる。

  * * * *

でも、使い方じゃなくて「作り方」の方に興味があるんですが、これが、ぜんぜんわからない。

http://d.hatena.ne.jp/kazu-yamamoto/20110413/1302683869

を見ると、

  data Identity a = Identity a deriving Show

  instance Monad Identity where
    return x = Identity x
    (Identity x) >>= f = f x
  

  > Identity 1 >>= \x -> return (x + 1)
  Identity 2

と使うと書いてある。これは、

  run1 y = do
     x <- Identity y
     return (x + 1)

こう書いても良いらしい。

  *Main> run1 3
  Identity 4

動くみたい。いや、これが、一体なんなのかが良くわからないんだけどさ。

  * * * *

それで、getChar の実装を探してみたんですが、

http://hackage.haskell.org/packages/archive/IOSpec/0.1/doc/html/src/Test-IOSpec-Teletype.html

かなぁ。もちろん、実際のコードではなくて、サンプル実装みたいな感じらしい。これを見ながら、

  data SignalState a =
     GetChar (Char -> SignalState a)   
   | ReturnTeletype a   


  instance Functor SignalState where
   fmap f (GetChar tt)    = GetChar (\x -> fmap f (tt x))
   fmap f (ReturnTeletype x) = ReturnTeletype (f x)

  instance Monad SignalState where
   return = ReturnTeletype
   (ReturnTeletype a) >>= g   = g a
   (GetChar f)     >>= g   = GetChar (\c -> f c >>= g)


  runTT (ReturnTeletype a) cs = Finish a
  runTT (GetChar f) (h:t)     = runTT (f h) (t)


  data Output a =
     Print Char (Output a)   
   | Finish a   deriving Show

  getChar1  =  GetChar ReturnTeletype

  run0 = runTT getChar1 "test"

と直してみると、

  *Main> run0
  Finish 't'

なんか、動いているっぽい。

  getll = do
    c <-getChar1
    if c=='\n'
      then return []
      else do
         l <- getll
         return (c:l)

  runn = runTT getll "test\n"
  runn1 = runTT getll "test\ntest\n"

で、それっぽく動いているようです。runTT が top level で、"test\n" とかを SignalState に隠して、実行しているわけね。

  *Main> runn1
  Finish "test"

Finish が付くのが謎だが。まぁ、良い。つうか、どう実行されているのか追うことが難しいんですけど。

いや、でも書きたいものは、また、少し別なんだけど…

No comments: