41
38

More than 5 years have passed since last update.

Functor, Applicative, Monadのシンプルな定式化

Last updated at Posted at 2016-05-14

HaskellとScalaのMonad関連について学び始めた頃に以下のブログ記事を読みました。

Functors, Monads, Applicatives – can be so simple | de.velopmind | The Det about Programming

Functor, Applicative, Monadといえば様々な角度やレベルから説明が可能と思われ、実際に多くの書籍や記事、ドキュメントで多くのことが語られていますが、このブログ記事は本質的な機能を(タイトル通り)とてもシンプルに説明していました。
直感的に理解しやすいと感じていたので、この内容を参考に補足説明とサンプルコードを加えて改めて整理してみました。
注意: シンプルに抽象化して統一的に理解することを目的としているため、Scala, Haskellの実際の言語仕様や実装レベルの詳細に正確には一致しない部分があります。

定式化

型パラメータを取る型Cの中身の型をAからBに変換するにはC[A] => C[B]という型の関数が必要となる。
CFunctor, Applicative, Monadのインスタンスであれば、以下の3通りの方法で関数C[A] => C[B]を得ることができる。

1. 型クラスFunctorのコア機能

(A => B) => (C[A] => C[B])

関数A => Bを関数C[A] => C[B]に変換する。

Scala Haskell
map fmap, <$>

2. 型クラスApplicativeのコア機能

C[A => B] => (C[A] => C[B])

関数C[A => B]を関数C[A] => C[B]に変換する。

Scala (scalaz) Haskell
ap, <*> <*>

3. 型クラスMonadのコア機能

(A => C[B]) => (C[A] => C[B])

関数A => C[B]を関数C[A] => C[B]に変換する。

Scala Haskell
flatMap >>=

適用例

Cの具体例としてScalaのOption、HaskellのMaybeを当てはめてみる。

1. A => Bという型の関数があるとき

CFunctorのインスタンスであればC[A] => C[B]が得られる。
例えば、

(Int => String) => (Option[Int] => Option[String])

Scala
val f: Int => String = x => x.toString
val n: Option[Int] = Some(1)

// Option#map を利用すると
n.map(f)

// (OptionはMonadでもあるので) for式で書くと
for {
  a <- n
} yield f(a)
Haskell
f :: Int -> String
f = \x -> show x
n :: Maybe Int
n = Just 1

-- fmap を利用すると
fmap f n

-- <$> を利用すると
f <$> n

-- (MaybeはMonadでもあるので) do記法で書くと
do
  a <- n
  return $ f a

2. C[A => B]という型の関数があるとき

CApplicativeのインスタンスであればC[A] => C[B]が得られる。
例えば、

Option[Int => String] => (Option[Int] => Option[String])

Scala
val f: Option[Int => String] = Some(x => x.toString)
val n: Option[Int] = Some(1)

import scalaz._, Scalaz._

// scalaz.Apply.ap を利用すると
Apply[Option].ap(n)(f)

// scalaz.syntax.ApplyOps.<*> を利用すると
n <*> f

// (OptionはMonadでもあるので) for式で書くと
for {
  a <- n
  g <- f
} yield g(a)
Haskell
f :: Maybe (Int -> String)
f = Just $ \x -> show x
n :: Maybe Int
n = Just 1

-- <*> を利用すると
f <*> n

-- (MaybeはMonadでもあるので) do記法で書くと
do
  a <- n
  g <- f
  return $ g a

3. A => C[B]という型の関数があるとき

CMonadのインスタンスであればC[A] => C[B]が得られる。
例えば、

(Int => Option[String]) => (Option[Int] => Option[String])

Scala
val f: Int => Option[String] = x => Some(x.toString)
val n: Option[Int] = Some(1)

// Option#flatMap を利用すると
n.flatMap(f)

// for式で書くと
for {
  a <- n
  b <- f(a)
} yield b
Haskell
f :: Int -> Maybe String
f = \x -> Just $ show x
n :: Maybe Int
n = Just 1

-- >>= を利用すると
n >>= f

-- do記法で書くと
do
  a <- n
  f a

Further Reading

HaskellのFunctor, Applicative, Monadについて箱のイメージで説明したブログ記事

Functors, Applicatives, And Monads In Pictures - adit.io

の翻訳です。
ここでいう「箱」が上述の型Cに相当します。

41
38
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
41
38