monad

函子:对map函数的泛化
在第一部分和第二部分实现了一些不同组合子库。这些组合子的相似性是值得注意的,比如为每个数据类型都实现了map函数,用于提升某个数据类型到上下文中的数据类型。例如下面的函数签名:

  def map[A, B](oa: Option[A])(f: A => B): Option[B] = ???
  def map[A, B](op: Parser[A])(f: A => B): Parser[B] = ???
  def map[A, B](og: Gen[A])(f: A => B): Gen[B] = ???

这些函数签名只是数据类型不同。这里将它定义为Scala trait并实现map:

trait Functor[F[_]] {

  def map[A, B](a: F[A])(f: A => B): F[B] 
}

函子法则
无论何时创建一个类似Functor的抽象,都不需要考虑实现哪些抽象方法,而是考虑遵循哪些法则。遵循什么样的法则完全由你决定,Scala不会强加任何这样的法则。对于Functor,将引入的法则是:

  map(x)(a => a) == x

Monad:对flatMap和unit函数的泛化
Functor只是众多抽象中的一个。Functor的作用不是那么显著,是因为仅仅使用一个map定义不出太多可用的操作。接下来我们介绍一个有趣的接口,monad。使用monad可以实现很多可用的操作,并且一劳永逸的对重复的代码进行重构。同时相关的法则可以推导库按照预期运行。现在我们为Monad定义一个Scala trait:

trait Monad[F[_]] extends Functor[F] {
  def unit[A](a: => A): F[A]

  def flatMap[A, B](a: F[A])(f: A => F[B]): F[B]

  override def map[A, B](a: F[A])(f: (A) => B): F[B] =
    flatMap(a)(a => unit(f(a)))

  def map2[A, B, C](fa: F[A], fb: F[B])(f: (A, B) => C): F[C] =
    flatMap(fa){a =>
      map(fb){b =>
        f(a, b)
      }
    }
}

练习 11.1

  val monadOption: Monad[Option] = new Monad[Option] {
    override def flatMap[A, B](a: Option[A])(f: (A) => Option[B]): Option[B] = a match {
      case None => None
      case Some(a) => f(a)
    }

    override def unit[A](a: => A): Option[A] = Some(a)
  }

  val monadStream: Monad[Stream] = new Monad[Stream] {
    override def flatMap[A, B](a: Stream[A])(f: (A) => Stream[B]): Stream[B] = a match {
        case Empty => Empty
        case head #:: tail => f(head) ++ flatMap(tail)(f)
      }

    override def unit[A](a: => A): Stream[A] = Stream(a)
  }

  val monadList: Monad[List] = new Monad[List] {
    override def flatMap[A, B](a: List[A])(f: (A) => List[B]): List[B] = a match {
      case Nil => Nil
      case head :: tail => f(head) ++ flatMap(tail)(f)
    }

    override def unit[A](a: => A): List[A] = List(a)
  }
  
  val monadPar: Monad[Par] = new Monad[Par] {
    override def flatMap[A, B](a: Par[A])(f: (A) => Par[B]): Par[B] =
      es => {
        val aa = a(es)
        f(aa.get())(es)
      }

    override def unit[A](a: => A): Par[A] = Par.unit(a)
  }

Monadic组合子
现在已经有Monad的原始语义了,回看之前的章节是否有其它为Monadic数据类型实现的函数可以统一被实现。
练习 11.3
大家已经很熟悉sequence和traverse的组合了,之前的章节很多地方都实现了它,现在使用Monad[F]来实现它们:

  def sequence[A](li: List[F[A]]): F[List[A]] = {
    def loop(n: Int, res: F[List[A]]): F[List[A]] = n match {
      case m if m < 0 => res
      case _ =>
        val temp = flatMap(res){la =>
          map(li(n)){a =>
            a :: la
          }
        }
        loop(n - 1, temp)
    }
    loop(li.length - 1, unit(Nil))
  }
  
  def traverse[A, B](la: List[A])(f: A => F[B]): F[List[B]] =
    sequence(la.map(f))

练习 11.4
实现replicateM。

  def replicateM[A](n: Int, ma: F[A]): F[List[A]] = {
    val la = List.fill(n)(ma)
    sequence(la)
  } 

练习 11.6
实现函数filterM,它看起来和filter类似,它接受的不是函数A=>Bollean,而是A=>F[Boolean]

  def filterM[A](la: List[A])(f: A => F[Boolean]): F[List[A]] = {
    def loop(n: Int, res: F[List[A]]): F[List[A]] = n match {
      case m if m < 0 => res
      case _ => 
        val temp = flatMap(res){li =>
          map(f(la(n))){b =>
            if(b) la(n) :: li
            else li
          }
        }
        loop(n - 1, temp)
    }
    loop(la.length - 1, unit(Nil))
  }

单子定律
毫无疑问functor法则对Monad是成立的,因为Monad[F]是一个Functor[F],但是除此之外呢?什么样的法则可以约束flatMap和unit?
结合法则

  x.flatMap(f).flatMap(g) == x.flatMap(a => f(a).flatMap(g))

KLEISLI组合:结合律更清晰的视图
Monad的结合法则看起来不是很清晰,幸运的是有种方法可以让它更清晰。不考虑F[A]类型monadic值,而是考虑A => F[B]的monadic函数。这样的函数称为Kleisli箭头。它们是可以相互组合的。
练习 11.7
实现Kleisli composition函数compose。

  def compose[A, B, C](f: A => F[B], g: B => F[C]): A => F[C] =
    a => flatMap(f(a))(g)

现在可以使用一个更加对称的方式为Monad声明结合法则了:

  compose(compose(f, g), h) == compose(f, compose(g, h))

单位元法则

  compose(f, unit) == f
  compose(unit, f) == f

练习 11.12
第三种monadic组合的最小集合map、unit和join。使用flatMap实现join。

  def join[A](mma: F[F[A]]): F[A] =
    flatMap(mma){ma => ma}

练习 11.14
使用join和map实现flatMap或compose。

  def flatMap[A, B](a: F[A])(f: A => F[B]): F[B] = 
    join(map(a)(f))

  def compose[A, B, C](f: A => F[B], g: B => F[C]): A => F[C] =
    a => join(map(f(a))(g))

什么是monadic
Monad和Monoid一样是一个更加抽象、纯代数的接口。Monad组合通常是一个Monad数据类型所有API中的一小部分,Monad不是对一个类型的泛化,而是大量不同的数据类型满足Monad接口和法则的抽象。两个Monad法则需要被满足结合法则(associativity)和单位元法则(identity),它们可以以不同的方式公式化。因此我们可以简单的描述Monad:

monad是一个满足associativity和identity法则的monadic组合的最小集的实现

identity monad
为了提炼monad的本质,先看一个有趣的例子,identity monad。

case class Id[A](value: A)

练习 11.17
为这个类型实现map和flatMap方法,并实现Monod[Id]。

case class Id[A](value: A) {
  
  def map[B](f: A => B): Id[B] = this match {
    case Id(a) => Id(f(a))
  }
  
  def flatMap[B](f: A => Id[B]) = this match {
    case Id(a) => f(a)
  }
}

  val monadId: Monad[Id] = new Monad[Id] {
    override def unit[A](a: => A): Id[A] = Id(a)

    override def flatMap[A, B](a: Id[A])(f: (A) => Id[B]): Id[B] = 
      a.flatMap(f)
  }

从上面的中可以看出,monad提供了一个引入和绑定变量的上下文,同时执行了变量替换。
状态monad和partial type application

  def stateMonad[S] = new Monad[({type f[X] = State[S, X]}) # f] {
    override def unit[A](a: => A): State[S, A] = State(s => (a, s))

    override def flatMap[A, B](a: State[S, A])(f: (A) => State[S, B]): State[S, B] =
      a.flatMap(f)
  }

你可能感兴趣的:(monad)