読者です 読者をやめる 読者になる 読者になる

by shigemk2

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

第2期 第21回 H本読書会 in 秋葉原 メモ #readhbon

Haskell 勉強会

readhbon.doorkeeper.jp

Endo

遠藤さんではない。自分自身に返る射を持つ圏のこと。

Data.Monoid

nineties.github.io

独習 Scalaz — 圏論

ドメインとコドメインが同一の対象の射を自己準同型射 (endomorphism) と呼ぶ。

http://nineties.github.io/category-seminar/2.html

実例

-- Endoの実例
import Data.Monoid
import Data.Foldable

main = do
    -- 左から順番に計算していく。
    print $ appEndo( foldMap (\x -> if even x then Endo (x *) else Endo(+ x)) [1,12,3,34,33] ) 0
    print $ appEndo( foldMap (\x -> if even x then Endo (x *) else Endo(+ x)) [1,12,3,34,32] ) 0
    print $ appEndo( foldMap (\x -> if even x then Endo (x *) else Endo(+ x)) [1,12,3,34,34] ) 0
    print $ ((((33*)34+)3*)12+1)

おさらい

P281-P290まで

  • 「モナド」は「アプリカティブファンクター」の強化版(アプリカティブファンクターはファンクターの強化版) どんどん柔軟性が高まる(三兄弟)
  • Maybeに特化したモナドの説明
  • Monad型クラス
Prelude> return "WHAT" :: Maybe String
Just "WHAT"
Prelude> 
  • ピエールの綱渡り(このネタは独習Scalazにも出てくる)
type Birds = Int
type Pole = (Birds, Birds)

-- 鳥
landLeft :: Birds -> Pole -> Pole
landLeft n (left, right) = (left + n, right)
landRight :: Birds -> Pole -> Pole
landRight n (left, right) = (left, right + n)

-- バランス棒
x -: f = f x

main = do
    print $ landLeft 2 (0, 0)
    print $ landLeft 1 (1, 2)
    print $ landRight (-1) (1, 2)
    print $ (0, 0) -: landLeft 1 -: landRight 1 -: landLeft 2

Data.Function

F#にあったものを流用したのが(&) らしい。

Prelude> Prelude.sum $ filter even $ map (+3) [1,2,3]
10

が、こういう書き方が出来るようになるらしい(GHC 7.10の場合)

Prelude> [1,2,3] & map (+3) & filter even & Prelude.sum

落ちるぅうあああ

4羽来た瞬間に落ちるパターンを実装する。具体的には、JustとNothingを使う。

type Birds = Int
type Pole = (Birds, Birds)

-- 鳥
landLeft :: Birds -> Pole -> Maybe Pole
landLeft n (left, right)
    | abs ((left + n) - right) < 4 = Just (left + n, right)
    | otherwise = Nothing
landRight :: Birds -> Pole -> Maybe Pole
landRight n (left, right)
    | abs (left - (right + n)) < 4 = Just (left, right + n)
    | otherwise = Nothing
-- バランス棒
x -: f = f x

main = do
    print $ landLeft 2 (0, 0)
    print $ landLeft 1 (1, 2)
    print $ landRight (-1) (1, 2)
    print $ landRight 10 (0, 3)

でもこういう書き方はできなくなった。

(0, 0) -: landLeft 1 -: landRight 1 -: landLeft 2

なので、実行方法も合わせる必要がある

*Main> :t (landRight 3 >>=)
(landRight 3 >>=) :: (Maybe Pole -> Pole -> b) -> Pole -> b

ピエールの綱渡り 複数の手順の方法

type Birds = Int
type Pole = (Birds, Birds)

-- 鳥
landLeft :: Birds -> Pole -> Maybe Pole
landLeft n (left, right)
    | abs ((left + n) - right) < 4 = Just (left + n, right)
    | otherwise = Nothing
landRight :: Birds -> Pole -> Maybe Pole
landRight n (left, right)
    | abs (left - (right + n)) < 4 = Just (left, right + n)
    | otherwise = Nothing
-- バランス棒
x -: f = f x

main = do
    print $ landLeft 2 (0, 0)
    print $ landLeft 1 (1, 2)
    print $ landRight (-1) (1, 2)
    print $ landRight 10 (0, 3)
    print $ landRight 1 (0, 0) >>= landLeft 2
    -- NothingがまじるとずっとNothing
    print $ Nothing >>= landLeft 2
    -- print $ (0, 0) -: landLeft 1 -: landRight 4 -: landLeft (-1) -: landRight (-2)
    -- の代わりの書き方
    -- returnは必須
    print $ return (0, 0) >>= landRight 2 >>= landLeft 2 >>= landRight 2
    -- ラムダを使った書き方
    print $ return (0, 0) >>= (\x -> landRight 2 x) >>= (\x -> landLeft 2 x) >>= (\x -> landRight 2 x)

アプリカティブとの比較

(+) <$> Just 3 <*> Just 4

アプリカティブだと、こういう書き方が出来ない

(+) <$> Nothing <*> Just 4 <*> Just 4
return (0,0) >>= landLeft 2 >>= landRight 2 >>= landRight 1

演算をどんどんつなげていくことができる。

バナナ

途中でバナナを踏んだら即死するデータ型を作る。 本にはないけど、ついでに、豆腐を踏んだらスタート地点にもどるやつも実装してみる

type Birds = Int
type Pole = (Birds, Birds)

-- 鳥
landLeft :: Birds -> Pole -> Maybe Pole
landLeft n (left, right)
    | abs ((left + n) - right) < 4 = Just (left + n, right)
    | otherwise = Nothing
landRight :: Birds -> Pole -> Maybe Pole
landRight n (left, right)
    | abs (left - (right + n)) < 4 = Just (left, right + n)
    | otherwise = Nothing
-- バランス棒
x -: f = f x
-- バナナ
banana :: Pole -> Maybe Pole
banana _ = Nothing
-- 豆腐
tofu :: Pole -> Maybe Pole
tofu _ = Just (0,0)

main = do
    print $ landLeft 2 (0, 0)
    print $ landLeft 1 (1, 2)
    print $ landRight (-1) (1, 2)
    print $ landRight 10 (0, 3)
    print $ landRight 1 (0, 0) >>= landLeft 2
    -- NothingがまじるとずっとNothing
    print $ Nothing >>= landLeft 2
    -- print $ (0, 0) -: landLeft 1 -: landRight 4 -: landLeft (-1) -: landRight (-2)
    -- の代わりの書き方
    -- returnは必須
    print $ return (0, 0) >>= landRight 2 >>= landLeft 2 >>= landRight 2
    -- ラムダを使った書き方
    print $ return (0, 0) >>= (\x -> landRight 2 x) >>= (\x -> landLeft 2 x) >>= (\x -> landRight 2 x)
    print $ (landRight 3 (1,2) >>= (\x -> landLeft 3 x))
    print $ landRight 3 (1, 2)
    -- バナナと豆腐
    print $ return (0, 0) >>= landLeft 1 >>= banana >>= landRight 1
    print $ return (0, 0) >>= landLeft 1 >>= tofu >>= landRight 1
    print $ return (0, 0) >>= landLeft 1 >>= landRight 1 >>= tofu

モナドを使わない選択

-- モナドを使わない選択
routine :: Maybe Pole
routine = case landLeft 1 (0, 0) of
              Nothing -> Nothing
              Just pole1 -> case landRight 4 pole1 of
                                Nothing -> Nothing
                                Just pole2 -> case landLeft 2 pole2 of
                                                  Nothing -> Nothing
                                                  Just pole3 -> landLeft 1 pole3

こういうふうに書かないといけないので、ネストの嵐になってむっちゃめんどう。

13.5 do記法

main = do
    print $ Just 3 >>= ( \ x -> Just (show x ++ "!"))
    print $ Just 3 >>= ( \ x -> Just "!" >>= ( \ y -> Just (show x ++ y)))
    -- print $ Nothing >>= ( \ x -> Just "!" >>= ( \ y -> Just (show x ++ y)))
    -- print $ Just 3 >>= ( \ x -> Just "!" >>= ( \ y -> Nothing))
    -- Just 3 >>= ( \ x -> Just "!" >>= ( \ y -> Nothing))
-- モナド値を連鎖させた式と等価なものを、ずっと簡潔に表せる記法が do 式

-- doを使わない書き方
foo1 :: Maybe String
foo1 = Just 3
      >>= ( \ x ->
            Just "!" >>= ( \ y ->
                           Just (show x ++ y)))

-- doを使った書き方
foo2 :: Maybe String
foo2 = do
    x <- Just 3
    y <- Just "!"
    Just (show x ++ y)

main = do
    print $ foo1
    print $ foo2

なんとなくわかるけど、自分流にコードをアレンジするの難しいね。

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!