Skip to content

Instantly share code, notes, and snippets.

@xuwei-k
Last active July 31, 2019 07:40
Show Gist options
  • Save xuwei-k/3769691 to your computer and use it in GitHub Desktop.
Save xuwei-k/3769691 to your computer and use it in GitHub Desktop.
モナドは合成できない( Translated Tony Morris's "Monads do not compose")

モナドは合成できない

http://blog.tmorris.net/monads-do-not-compose/

わたしは、Scalaのメーリングリストで、「モナドは合成できない」といいました。これは正確にはどういう意味でしょうか?たぶん、以下が答えです。

まず最初に、FunctorApplicative そして Monad のインターフェイスから話をはじめましょう。

trait Functor[F[_]] {
  def fmap[A, B](f: A => B, a: F[A]): F[B]
}
 
trait Applicative[F[_]] extends Functor[F] {
  def ap[A, B](f: F[A => B], a: F[A]): F[B]
  def point[A](a: A): F[A]
  override final def fmap[A, B](f: A => B, a: F[A]) =
    ap(point(f), a) 
}
 
trait Monad[F[_]] extends Applicative[F] {
  def flatMap[A, B](f: A => F[B], a: F[A]): F[B]
  override final def ap[A, B](f: F[A => B], a: F[A]) =
    flatMap((ff: A => B) => fmap((aa: A) => ff(aa), a), f)
}

主張を言い換えてみましょう。

  1. Functor は合成できる
  2. Applicative は合成できる
  3. Monad は合成できない

では、1と2について話しましょう。この文脈で、「Xが合成できる」とはなにを意味するでしょうか?この疑問について、さらに簡潔に答えてみましょう。

「Xが合成できる」とは、あるXについて、以下のようなシグネチャの関数が書けるということを意味します。

def XCompose[M[_], N[_]](implicit mx: X[M], nx: X[N]): 
     X[({type λ[α]=M[N[α]]})#λ]

この戻り値型は、わかりにくく見えるかもしれません。しかし、これは単にScalaにおいて、型コンストラクタを部分適用する方法に過ぎません。 It is essentially the X type constructor applied to the composition of M and N and followed by a type variable just like M and N itself (kind * -> *). 私達の主張をテストしてみましょう。

Functorは合成できます。つまり、以下のようなシグネチャの関数を書くことができます

def FunctorCompose[M[_], N[_]]
    (implicit mx: Functor[M], nx: Functor[N]): 
        Functor[({type λ[α]=M[N[α]]})#λ]

やってみましょう!

new Functor[({type λ[α]=M[N[α]]})#λ] {
  def fmap[A, B](f: A => B, a: M[N[A]]) =
      mx.fmap((na: N[A]) => nx.fmap(f, na), a)
}

2つのFunctorを合成することができました。1の主張について実証しました。では、2についてはどうでしょうか?やってみましょう!

def ApplicativeCompose[M[_], N[_]]
    (implicit ma: Applicative[M], na: Applicative[N]):
        Applicative[({type λ[α]=M[N[α]]})#λ] =
  new Applicative[({type λ[α]=M[N[α]]})#λ] {
  def ap[A, B](f: M[N[A => B]], a: M[N[A]]) = {
    def liftA2[X, Y, Z](f: X => Y => Z, a: M[X], b: M[Y]): M[Z] =
      ma.ap(ma.fmap(f, a), b)
    liftA2((ff: N[A => B]) => (aa: N[A]) => na.ap(ff, aa), f, a)
  }
  def point[A](a: A) =
    ma point (na point a)
}

ちょっと、本質的ではない(型についての)記述が多いですが、しかし、このようにScalaは大量のボイラープレートを要求することが多いです。うまくいけば、あなたは 「2つの Applicative Functor を合成する」ということが、何を意味するのかを理解することができるでしょう。では、Monadの話に戻りましょう。本質的に、「あなたは、以下のようなMonadを合成する関数を記述することが不可能」ということです。

def MonadCompose[M[_], N[_]](implicit ma: Monad[M], na: Monad[N]):
    Monad[({type λ[α]=M[N[α]]})#λ]

これがお役に立てれば幸いです!

@ssmylh
Copy link

ssmylh commented Sep 23, 2012

It is essentially the X type constructor applied to the composition of M and N and followed by a type variable just like M and N itself (kind * -> *).

についてですが、文法的には、itは前文のitと同じものを指していていて、applied to〜とfollowed by〜が、the X type constructorにかかっています。
なので直訳すると、

これは本質的には、MとNの合成に適合し、そして(カインドが * -> *である)ちょうどMやNそれ自身のような型変数に準ずる、X型コンストラクタです。

な感じかと思います。
意味合い的には、
「これ(前文からのit)」は、本質的に、MとNの合成を表し、MとNと同じ1階カインドである、X型コンストラクタ、だよと言いたいのだと思います。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment