前回
- IOもアプリカティブファンクター
- 関数もアプリカティブ
- リストをアプリカティブファンクターにする方法は複数ある
- アプリカティブ則(いくつかの条件を満たさないとアプリカティブにならない)
- ファンクター(写像)
11.4 アプリカティブの便利な関数
- Control.Applicative には liftA2 がある
- 通常のファンクターでは、関数を 1 つのファンクター値に適用することしかできない
- liftA2 は通常の 2 引数関数を、 2 つのアプリカティブ値を引数に取る関数に昇格させる関数
Prelude> fmap ( \ x -> [x]) (Just 4) Just [4]
Prelude> import Control.Applicative Prelude Control.Applicative> :t liftA2 liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
Prelude Control.Applicative> liftA2 (:) (Just 3) (Just [4]) Just [3,4] Prelude Control.Applicative> (:) <$> Just 3 <*> Just [4] Just [3,4]
こういうふうにアプリカティブ値から単一のアプリカティブ値を組み立てることが出来る。再帰でsequenceAを呼び出している。
import Control.Applicative sequenceA :: (Applicative f) => [f a] -> f [a] sequenceA [] = pure [] sequenceA (x:xs) = (:) <$> x <*> sequenceA xs main = do print $ sequenceA [Just 1, Just 2]
Prelude Control.Applicative> sequence [Just 1, Just 2] Just [1,2] Prelude Control.Applicative> sequence [Just 1, Just 2, Just 3] Just [1,2,3] Prelude Control.Applicative> sequence [Just 1, Just 2, Just 3, Just 4] Just [1,2,3,4]
畳み込みを使ったsequenceA
import Control.Applicative sequenceA :: (Applicative f) => [f a] -> f [a] sequenceA = foldr (liftA2 (:)) (pure []) main = do print $ sequenceA [Just 1, Just 2] print $ sequenceA [Just 3, Just 2, Just 1] print $ sequenceA [Just 3, Nothing, Just 1] print $ sequenceA [[1,2,3],[4,5,6]] print $ sequenceA [[1,2,3],[4,5,6],[3,4,4],[]] -- equal print $ (:) <$> Just 1 <*> sequenceA [Just 2] print $ (:) <$> Just 1 <*> ((:) <$> Just 2 <*> sequenceA []) print $ (:) <$> Just 1 <*> ((:) <$> Just 2 <*> Just []) print $ (:) <$> Just 1 <*> Just [2]
なんとなくのアプリカティブ
Prelude Control.Applicative> (++) <$> Just [1] <*> Just [2] Just [1,2] Prelude Control.Applicative> sequence [(+3), (+2), (+1)] 3 [6,5,4]
関数に対するsequenceの適用
Prelude Control.Applicative> map ( \ f -> f 7) [(>4),(<10),odd] [True,True,True] Prelude Control.Applicative> and $ map ( \ f -> f 7) [(>4),(<10),odd] True
空リスト内包表記
[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]] Prelude Control.Applicative> [[x,y] | x <- [1,2,3], y <- [4,5,6], z <- []] [] Prelude Control.Applicative> [[x,y,z] | x <- [1,2,3], y <- [4,5,6], z <- []] []
I/Oアクションもsequenceでまとめることが出来る
非決定性計算 http://sicp.iijlab.net/fulltext/x430.html
Prelude Control.Applicative> sequence [getLine, getLine] hello world ["hello","world"]
まとめ
プリカティブファンクターを使えば、I/Oを伴う計算、非決定性計算、失敗するかもしれない計算、......などなど、多種多様な計算をアプリカティブ・スタイルを使って組み合わせることができる
12 モノイド
- 便利で楽しい型クラス
- モノイドは、値を二項演算子で結合できるような型
- newtype キーワードはしょっちゅう使う
12.1 既存の型を新しい型にくるむ
Prelude Control.Applicative> getZipList $ ZipList [(+1),(*100),(*5)] <*> ZipList [1,2,3] [2,200,15]
newtypeキーワード
Haskell の newtype キーワードは、まさにこのような1つの型を取り、それ を何かにくるんで別の型に見せかけたいという場合のために作られたもの
- newtypeはdataに比べて高速
- 持てる値コンストラクタの数は1種類だけ フィールドも1つだけ
dataと同じようにderivingで自動導出できる
newtype を使って型クラスのインスタンスを作る
newtype で作った型にはパターンマッチも使える
newtype と遅延評価
- newtype でできるのは、既存の型を新しい型に変えることだけ
- Haskellはデフォルトで遅延評価
Prelude Control.Applicative> head [3,4,5,undefined,2,undefined] 3
newtypeの使い方
-- data CoolBool = CoolBool { getCoolBool :: Bool } newtype CoolBool = CoolBool { getCoolBool :: Bool } helloMe :: CoolBool -> String helloMe (CoolBool _ ) = "hello" main = do print $ helloMe undefined
dataとnewtypeは違うよねっていう話。
type vs newtype vs data
- typeは型シノニムを作る シノニムはシノニムなので互換性があるだけ。
- newtypeキーワードは、既存の型を包んで新しい型を作るためのもの
- dataキーワードは、自作の新しいデータ型を作るためのもの
newtype CharList = CharList { getCharList :: [Char] }
CharListとCharをリスト連結することは不可能。
- typeは型シノニム
- newtypeは既存の型をある型クラスのインスタンスにしたくて、新しい型にくるむ方法を探すやつ
- dataは何かまったく新しいものを作りたいやつ
12.2 Monoid大集合
Haskell の型クラスは、同じ振る舞いをする型たちに共通のインターフェイスを提供するために使われている→どんな共通点があるだろうか。
モノイド→結合的な性質があること(数学的な概念)
モノイドは圏論の一種
semigroupは半群
Mekanik Destruktiw Kommandoh [12 inch Analog]
- アーティスト: Magma
- 出版社/メーカー: Jazz Village
- 発売日: 2015/06/09
- メディア: LP Record
- この商品を含むブログを見る