実例によるPureScript 8 章 - 1

モナドが登場した。課題がムズくて時間がかかりそうなので分割することにする。

1.(簡単) purescript-arraysパッケージの Data.Arrayモジュールから head関数と tail関数の型を探してください。 Maybeモナドとdo記法を使い、 headと tailを組み合わせて、3要素以上の配列の3番目の要素を返すような関数を作ってください。その関数は適当な Maybe型を返さなければいけません。

thirdElement :: forall a. Array a -> Maybe a
thirdElement xs = do
  a <- tail xs
  b <- tail a
  c <- head b
  pure c


2.(やや難しい) 与えられた幾つかの硬貨を組み合わせてできる可能性のあるすべての合計を決定する関数 sumを、 foldMを使って書いてみましょう。入力の硬貨は、硬貨の価値の配列として与えられます。この関数は次のような結果にならなくてはいけません。

難しい。期待する答えを返していると思うが、自分の書いたコードを見てもまだよく理解できていない。。。Array の挙動が特殊すぎる。

sums :: Array Int -> Array Int
sums xs = sort $ nubBy compare (foldM (\a b -> [a, a + b]) 0 xs)


3.(やや難しい) Maybe型構築子について、 ap関数と apply演算子が一致することを確認してください。

apMaybe :: forall a b. Maybe (a -> b) -> Maybe a -> Maybe b
apMaybe f a = ap f a

applyMaybe :: forall a b. Maybe (a -> b) -> Maybe a -> Maybe b
applyMaybe f a = f <*> a

上記はコンパイルする。型も確かに一致。

> :t apMaybe  
forall (a :: Type) (b :: Type). Maybe (a -> b) -> Maybe a -> Maybe b

> :t applyMaybe
forall (a :: Type) (b :: Type). Maybe (a -> b) -> Maybe a -> Maybe b

これでいいんかな。

4.(やや難しい) purescript-maybeパッケージで定義されている Maybe型についての Monadインスタンスが、モナド則を満たしていることを検証してください。

expr1 :: Maybe Int
expr1 = Just 4

maybeRightIdentityLaw1 :: Maybe Int
maybeRightIdentityLaw1 = do
  x <- expr1
  pure x

maybeRightIdentityLaw2 :: Maybe Int
maybeRightIdentityLaw2 = expr1

y :: Int
y = 4

maybeLeftIdentityLaw1 :: Maybe Int
maybeLeftIdentityLaw1 = do
  x <- pure y
  pure (x * x)

maybeLeftIdentityLaw2 :: Maybe Int
maybeLeftIdentityLaw2 = pure (y * y)

m1 :: Maybe Int
m1 = Just 4

m2 :: Int -> Maybe Int
m2 x = pure (x * x)

m3 :: Int -> Maybe Int
m3 x = pure (x * x)

maybeAssociativityLaw1 :: Maybe Int
maybeAssociativityLaw1 = do
  y <- do
    x <- m1
    m2 x
  m3 y

maybeAssociativityLaw2 :: Maybe Int
maybeAssociativityLaw2 = do
  x <- m1
  y <- m2 x
  m3 y

出力が一致することを確認した。

main :: Effect Unit
main = do
  logShow $ maybeRightIdentityLaw1
  logShow $ maybeRightIdentityLaw2
  logShow $ maybeLeftIdentityLaw1
  logShow $ maybeLeftIdentityLaw2
  logShow $ maybeAssociativityLaw1
  logShow $ maybeAssociativityLaw2


5.(やや難しい) 配列上の filterの関数を一般化した関数 filterMを書いてください。この関数は次の型シグネチャを持つ必要があります。
filterM :: forall m a. Monad m => (a -> m Boolean) -> List a -> m (List a)
PSCiで Maybeと Arrayモナドを使ってその関数を試してみてください。

めちゃくちゃ難しかった。GitHub で検索してヒントというかほぼ答えを見た。感覚だが、do の中では <- で代入された変数は最後の m のコンテキストになるイメージ。b は m Boolean なので Boolean として扱える。xs' は m (List a) なので List a として扱える。多分最後は pure じゃなくて return の方が良い気がする。

filterM :: forall m a. Monad m => (a -> m Boolean) -> List a -> m (List a)
filterM _ Nil = pure Nil
filterM f (x:xs) = do
  b <- f x
  xs' <- filterM f xs
  if b == true
    then pure (x : xs')
    else pure (xs')


6.(難しい) すべてのモナドは、次で与えられるような既定の Functorインスタンスがあります。

map f a = do
x <- a
pure (f x)
モナド則を使って、すべてのモナドが次を満たすことを証明してください。

lift2 f (pure a) (pure b) = pure (f a b)
ここで、 Applicativeインスタンスは上で定義された ap関数を使用しています。 lift2が次のように定義されていたことを思い出してください。

lift2 :: forall f a b c. Applicative f => (a -> b -> c) -> f a -> f b -> f c
lift2 f a b = f <$> a <*> b

最初の $${f <$> a}$$ が適用されると $${pure (f a)}$$ になり、その後 $${<*>}$$ が適用されると $${pure (f a b)}$$ になる、でダメかな。

この記事が気に入ったらサポートをしてみませんか?