2015年5月31日日曜日

アルチュハイマーな脳でも、モナドが何となく分かってきたような気がする

Real World Haskellの15.4章の「配管を隠す遊び」で掲載されているコードをちょっと弄ってみたものの、これであっているかどうかいまいち自信が持てないためブログに晒してみました
内容はStateモナドの詳細を隠すためのコードの書き方が掲載されています、そのまま直書きですとこんな感じ、
module Supply
       (
         Supply
       , next
       , runSupply
       ) where

import Control.Monad.State

newtype Supply s a = S (State [s] a)

unwrapS :: Supply s a -> State [s] a
unwrapS (S s) = s

instance Monad (Supply s) where
  s >>= m = S (unwrapS s >>= unwrapS . m)
  return = S . return

runSupply :: Supply s a -> [s] -> (a, [s])
runSupply (S m) xs = runState m xs

next :: Supply s (Maybe s)
next = S $ get >>= \st ->
             case st of
               [] -> return Nothing
               (x:xs) -> put xs >>= \_ -> return (Just x)
サンプルはnext関数を呼ぶとStateモナドが持っている状態のリストから1つづつ値を返してくるというコード、こうやってSupplyモナドを走らせる
*Supply> runSupply next [1,2,3,4,5]
(Just 1,[2,3,4,5])
これはこれでいいのですが、ghc-7.8.3で実行するとこんなエラーがでる
GHCi, version 7.8.3: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> :load "/home/cuomo/Code/haskell/State/Supply5.hs"
[1 of 1] Compiling Supply      ( /home/cuomo/Code/haskell/State/Supply5.hs, interpreted )

/home/cuomo/Code/haskell/State/Supply5.hs:15:10: Warning:
    ‘Supply’ is an instance of Monad but not Applicative - this will become an error in GHC 7.10, under the Applicative-Monad Proposal.
Ok, modules loaded: Supply.
*Supply> 
「ghc-7.10だとエラーになるからSupplyのApplicativeインスタンスを書けよ」って脅かされる、なので自分でインスタンスを適当に書いてみた

...
import Control.Applicative
...

instance Applicative (Supply s) where
   pure = return
   (S mf) <*> (S m) = undefined
そしたら今度は「Functor書け」って怒られる始末、なのでいわれるがままにFunctorを書く
instance Functor (Supply s) where
  fmap f (S m) = undefined
これで、構文チェックはOKらしいが「undefined」なので詳細をつめる
instance Functor (Supply s) where
  fmap f (S m) = S $ f <$> m

instance Applicative (Supply s) where
   pure = return
   (S mf) <*> (S m) = S $ mf >>= \f -> f <$> m
これで警告やエラーはとまったがFuntor則にあってるかどうか確かめないとわからないので、確認してみる
第1法則「fmap id = id」の確認
*Supply> runSupply (id `fmap` next) [1,2,3,4,5] == runSupply next [1,2,3,4,5]
True
良さげ...
第2法則「fmap (f . g) = fmap f . fmap g」の確認
*Supply> :m +Data.Maybe
*Supply Data.Maybe> runSupply (fmap (id . fromJust) next) [1,2,3,4,5] == runSupply ((fmap id . fmap fromJust) next) [1,2,3,4,5]
True
という感じでいいのでしょうか?
next関数がMaybe値を返してくるのでfromMaybeを使って試してみました
そうするとこんな感じで使えるApplicativeだと
*Supply> runSupply (return (liftM (+1)) <*> next) [1,2,3,4,5]
(Just 2,[2,3,4,5])
何となくしっくり来た感があるのですが間違っていないでしょうか...ちなみに「アル中ハイマー」「アルチュハイマー」は別物です、どちらかといえば「アルチュハイマー」の方がかわいいです。



アルチュハイマーな型はこちらをどうぞ

0 件のコメント:

コメントを投稿