by shigemk2

当面は技術的なことしか書かない

第2期 第18回 H本読書会 in 秋葉原 まとめ #readhbon

readhbon.doorkeeper.jp

前回

  • 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 の型クラスは、同じ振る舞いをする型たちに共通のインターフェイスを提供するために使われている→どんな共通点があるだろうか。

  • モノイド→結合的な性質があること(数学的な概念)

モノイド - Wikipedia

モノイドは圏論の一種

圏論 - Wikipedia

semigroupは半群

半群 - Wikipedia

マグマ (数学) - Wikipedia

Mekanik Destruktiw Kommandoh [12 inch Analog]

Mekanik Destruktiw Kommandoh [12 inch Analog]