LoginSignup
0
0

More than 5 years have passed since last update.

Haskellについてちょっとだけ調べてみた

Posted at

このへん
* 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
  • IOやMaybeはfunctorかつApplicativeかつMonadである
  • おもしろいことに、や((->) r)(関数)もfunctorである
  • 関数のfmapは関数合成と同じ

fmapがあることにより、既存の関数を変更することなく(オペレータを変更することにより)引数に通常の値でもfunctorでも与えることができる。

ただこれだと、2引数をとるような関数aに対してfmapすると (a -> b -> c) <$> f af (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

感想

  • こういう書き方をさせていくことによって、処理を書いてるというよりは定義を書いてる感じになってくるんだろう
  • 新たな体系を記述していくのはそれはそれで楽しいかもしれない
  • モナドがなにするもんなのかなんとなく分かってよかった
  • ライプニッツのモナドとは何の関係もなかった
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0