ゴイスーなえっち本を読んだのでHaskellについてまとめた
Haskellメモ
すごいH本を読んだのでHaskellに関してメモしたことをそのまんまここに残しておく。違うところもあるかも...
基本的なこと(データ型など)については省略
すばらしい機能
パターンマッチ
- 与えられた引数に対してパターンマッチを行える
例) フィボナッチ数を求める(パターンマッチver.)
fib :: Int -> Int fib 0 = 1 fib 1 = 1 fib x = fib (x - 1) + fib (x - 2)
ガード
- 与えられた引数に対して条件式を行える
例) フィボナッチ数(ガード文ver.)
fibG :: Int -> Int fibG x | n == 0 -> 1 | n == 1 -> 1 | otherwise -> fibG (n - 1) + fibG (n - 2)
case .. of ...
- 与えられた引数に対してパターンマッチと条件式を行える
例) フィボナッチ数(case of ver.)
fibC :: Int -> Int fibC n = case n of 0 -> 1 1 -> 1 _ | n > 1 -> fibC (n - 1) + fibC (n - 2) {-- case ofは上から評価されるので本来ガード文はいらない? --}
Haskellはこのように文法は違うものの、同じ機能を持たせることができる。使い方は見やすさなどを考慮して使い分ける
特殊な式について
do
Monad値をつくるための式
let .. in
let
で関数を、in
で式を定義する式
どこにでも定義できる
例) リスト内包式
[let inc x = x + 1 in (inc 1)] {-- [2] (xを引数にとり+1して返す関数に対して1を引数に適用する式をListに内包して返す) --}
where
変数や関数を定義された式に対して束縛することができる
使い方はlet .. in
とさほど変わらないので関数の定義によって見やすさのために使い分けるべき
return
Monadを返す。CやJavaのようなプログラミング言語とは異なって結果の返り値や処理の抜け出しとしての使用法ではなく、正しく返されるべきMonadにラップされて値が返される
例) mainはIO ()
が返るべき
main :: IO () main = do let a = return "hello" {-- aの値はMonadである --} let b = "world" {-- bの値はStringである --} a >>= print {-- `>>=`の左辺が受け付ける引数はMonadのみ。print関数を呼び出したのでaはIOモナドとして関数に適用される --} print $ b {-- print関数は文字として表せるもののみ引数にとる(Monadは引数にとれない) --} print $ a {-- printはMonadを引数に取れないので構文エラーとなる --}
Functorとは?Applicativeとは?Monoid?Monad?ん?
これらをざっくりいうと、型が型を引数にもつ(型引数)もの。つまり型を持つことのできる型クラス
すごいH本がいうところの文脈付きの型
またそれらがもつ値をあらゆる形に変更・加工することのできるもの
例) Maybe型やList型([]
)はこれらすべてのインスタンスである
Maybe Int
→ Int
型を持つMaybe
という型
[Int]
→ Int
型を持つ[]
という型
このような型を引数(型引数)に新しい型を作る型(型コンストラクタ)は下に示した機能をもつことができる
前提のお話
同様に、文脈付き関数もまた直接関数を使用することが出来ない(
Maybe (a -> b)
という関数はa -> b
を直接適用することが出来ない)
Functor
文脈付き型に対して関数を適用することのできるもの
Haskellではfmap(<$>)
がFunctorに属する
例)
fmap (\x -> x + 1) (Just 1) {-- Just 2 --} (+1) <$> (Just 1) {-- Just 2 --} (+2) <$> Nothing {-- Nothing(Maybeの仕様より、値のない`Nothing`が返る) --}
Applicative
文脈を持つ値と関数同士を適用することができるもの
Haskellでは中置関数で<*>
がある
例)
(Just (+3)) <*> (Just 4) {-- Just 7 (与えられた引数に対して+3をする関数に4を与える) --} Nothing <*> (Just 4) {-- Nothing(適用される関数がないので`Nothing`) --} (Just (+3)) <*> Nothing {-- 逆も然り --}
Monoid
文脈付き型を結合することができるもの
例)
[1, 2, 3] `mappend` [4, 5, 6] {-- [1, 2, 3, 4, 5, 6] --} Just [1, 2, 3] `mappend` Just [4, 5, 6] {-- Just [1, 2, 3, 4, 5, 6] (Maybeのインスタンスである場合、中身を結合することができる) --}
Monad
文脈付き型に対して文脈付き型を返すことができるもの
これまでのFunctor、Applicative、Monoidは振る舞いが違うものの、全て文脈付き型を授かり文脈付き型を返すことに関しては全部一緒である
- つまりMonadはFunctorにもApplicativeにもMonoidにもなり得るもの??
Monadはreturn
と>>=
、>>
という関数を持つとすごいH本には書いてある
例)
return $ Just 3 {-- Just 3 (returnはClangやJavaの`return文`とは異なり、Monadをそのまま返す`関数`である) --} Just 3 >>= \x -> return $ x + 3 {-- Just 6 (`>>=`は左辺のモナドから結果だけを取り出し右辺の関数に適用した結果を新しいMonadとして返す) --} Nothing >>= \x -> return $ x + 3 {-- Nothing (MaybeはMonadなので`>>=`を適用できる) --} Just 3 >> Just 4 {-- Just 4 (両方がJustの場合、右を評価する) --} Nothing >> Just 4 {-- Nothing (言わずもがな) --}
↑たちのルール(XX則)
Functor則
- idでファンクター値を写した場合、ファンクター値が変化してはいけない
例)
fmap id (Just 3) {-- Just 3 --} fmap id [1 .. 5] {-- [1, 2, 3, 4, 5] --} fmap id Nothing {-- Nothing --}
「fとgの関数合成でファンクター値を写したもの」と「g、fと順番にファンクター値を写したもの」が等しいこと
fmap (f. g) = fmap f . fmap g
であること
Applicative則
以下の式は等価である
(pure
は純粋にその関数を返す関数)
pure f <*> x = fmap f x
pure id <*> v = v
pure (.) <*> u <*> v <*> w = u <*> (v <*> u)
pure f <*> pure x = pure (f x)
u <*> pure y = pure ($ y) <*> u
Monoid則
以下の式は等価である
mempty
mappend
x = x- 空のモノイド値に対してモノイド値を結合するとき、モノイド値が評価されること
x
mappend
mempty = x- モノイド値に対して空のモノイド値を結合するとき、モノイド値が評価されること
(x
mappend
y)mappend
z = xmappend
(ymappend
z)- 3つのモノイド値を結合するとき、それぞれの結合する順番に関係なく結果が等しくあること
Monad則
左恒等則
return
を使ってデフォルトの値をデフォルトの文脈に入れたものを>>=
を使って関数に食わせた結果は、単にその値にその関数を適用した結果と等しくあることreturn x >>= f
とf x
は等価である
右恒等則
モナディック関数
モナディック関数とは
モナド値を操作したり、モナド値を返したりする関数(両方も可!)
すごいH本より
Listは文脈付き型であるためMonadであり、Monad値を操作できる関数(map、filter...)はモナディック関数である
関数の紹介
MonadはApplicative Functorであり、Applicative FunctorはFunctorである
Monad > Applicative > Functror
liftM
ap
<*>
の強化版?<*>
-> Applicativeの関数と値同士を適用できるap -> Monadの関数と値同士を適用できる
liftA2
- 関数を2つのアプリカティブ値に適用できる
liftM2, liftM3 ...
join