このへん
* http://learnyouahaskell.com/chapters
* http://www.seas.upenn.edu/~cis194/lectures.html
Haskellの特徴
- 静的型付け
- 遅延評価
- 型推論
- Algebraic data type
- 型を非常に柔軟に作れる
- enumみたいのも簡単にできる
- 再帰的な定義も簡単にできる
- curry
- 例えば
a -> b -> c
な関数があったとすると、これは2引数の関数なのではなく、1つの引数を取り、さらにもう一つ引数を取ったらc型を返す関数である
- 例えば
- パターンマッチ
- 型クラス
- Haskellのclass宣言はJavaで言うとinterfaceのようなもの
- Haskellのinstance宣言は、Javaだとinterfaceを実装したclass(メソッドのみ)のようなもの
- functor, monad...
FunctorとApplicative
class Functor f where
fmap :: (a -> b) -> f a -> f b
<$> = fmap
class (Functor f) => Applicative f where
pure :: a -> f a
(<*>) = f (a -> b) -> f a -> f b
fmapがあることにより、既存の関数を変更することなく(オペレータを変更することにより)引数に通常の値でもfunctorでも与えることができる。
ただこれだと、2引数をとるような関数aに対してfmapすると (a -> b -> c) <$> f a
でf (b -> c)
が返ってくるのでFunctorに定義されているパーツだけでは処理できない。Applicativeではこれ用に<*>
が定義されており、これでようやくパーツが揃うことになる.
(a -> b -> c) <$> f a <*> f b
引数がもっとあれば、引数の分だけ<*>
を増やしてけばよい.
Monoid
class Monoid m where
mempty :: m
mappend :: m -> m -> m
mconcat :: [m] -> m
maconcat = foldr mappend mempty
- 単位元と二項演算が定義されている。
- Monoidのinstanceを定義することにより系を定義できる。
- 新しい演算の体系を定義できる…!!
FunctorとかApplicativeとかMonadとかMonoidとか、Haskellではなるべく値を
値をそのものとして扱うのではなく、値をあるcontext内に入れ、contextのレベル
で扱おうとしているように見える。(Javaで例えると、値を生のまま引き回すじゃなくてクラス作ってそん中に入れとくのとやりたいことは似ているっちゃ似ている)
Monad
class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> mb
(>>) :: m a -> m b -> m b
x >> y = x >>= \_ -> y
fail :: String -> m a
fail msg = error msg
- returnはApplicativeのpureと同じ役割(他の言語におけるreturnとは全く関係ない)
- []やMaybeはMonadである
instance Monad Maybe where
return x = Just x
Nothing >>= f = Nothing
Just x >>= f = f x
fail _ = Nothing
戻り値としてMaybeを返したい場合は多々あるし、入出力系だとIOを返さざるを得ないような場合があって、 a -> m a
な関数を書きたい場合が必ず出てくる。
そんなこんなで、(a -> m a)
の結果を次の(a -> m a)
にシームレスに入れたくなってくる。
そこで>>=
を使うと、(a -> m a) >>= (a -> m a) >>= ...
のように書ける。
-
do
内の<-
は、>>=
のsyntax sugarである。 -
do
内でx <- y
のパターンマッチが失敗すると、即座にそのMonadについてfail
で定義された値が返る。
foo :: Maybe String
foo = do
x <- Just 3
y <- Just "!"
Just (show x ++ y)
-- 以下と同じ
foo = Just 3 >>= (\x ->
just "!" >>= (\y ->
Just (show x ++ y)))
-- Monad版のfmap
LiftM :: (Monad m) => (a -> b) -> m a -> m b
-- Monad版のfilter
filterM :: (Monad m) => (a -> m Bool) -> [a] -> m [a]
-- Monad版のfoldl
foldM :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a
感想
- こういう書き方をさせていくことによって、処理を書いてるというよりは定義を書いてる感じになってくるんだろう
- 新たな体系を記述していくのはそれはそれで楽しいかもしれない
- モナドがなにするもんなのかなんとなく分かってよかった
- ライプニッツのモナドとは何の関係もなかった