monad

作者: 吐思圈 | 来源:发表于2018-03-18 00:07 被阅读0次

    函子:对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

          本文链接:https://www.haomeiwen.com/subject/whvkqftx.html