Parsec 的 Map 和 FlatMap

Parsec 的 Map 和 FlatMap

上一篇文章《Parsec算子的类型错误》中,我提到可以提供和 map 与 flatMap 相关的算子,并给出了示例。

但是在实际的各个 Parsec 移植项目中,我并没有提供这样的算子。

因为在 FP 的视角,map 和 flatMap 是比 Parsec 组合子更基础的概念。它们是任何 Monad 都应该支持的东西。对于 Haskell 原版,我们实际上是针对 parsec 算子的计算结果 Either[Left, Right] 做 map 和 flatMap,所以 Parsec 本身并不需要额外考虑这种问题。并且因为 curry 化和惰性求值的效果,看起来我们是在直接操作 parsec 算子。例如

readExpr input = case parse (spaces >> symbol) "lisp" input of
    Left err -> "No match: " ++ show err
    Right val -> "Found value

这段代码来自《Write Yourself a Scheme in 48 Hours》,这里的 >>其实可以完全与 Parsec 无关,使用 Parsec 的计算结果 Either 类型组合。但是在 Scala 和 Java 的版本中,我始终还是不太敢做如此激进的实现(虽然语法上说,Scala 允许我把state 实现为一个 parsec 的隐式参数),因此让 Parsec 实现 Monad ——也就同时包含了 applicative 和 functor 。其实是一个出于实用考虑的妥协。

所以目前,我们在 Jaskell Core 中,将 Parsec 封装成一个特殊的 Monad:

   implicit def mkMonad[T]: Monad[({
    
    type P[A] = Parsec[T, A]})#P] =
      new Monad[({
    
    type P[A] = Parsec[T, A]})#P] {
    
    
        override def pure[A](element: A): Parsec[T, A] = Return(element)

        override def fmap[A, B](m: Parsec[T, A], f: A => B): Parsec[T, B] = m.ask(_).map(f)

        override def flatMap[A, B](m: Parsec[T, A], f: A => Parsec[T, B]): Parsec[T, B] = state => for {
    
    
          a <- m.ask(state)
          b <- f(a).ask(state)
        } yield b
      }
  }

相信有 Haskell 经验的同行能够看出,这并不是一个很纯粹的实现,只是一个妥协,在不需要将 state 参数 curry化(或者implicit)的前提下,尽可能模拟原版的形式。这样也就可以做到 map 和 flatMap ,仍然是 Monad ,而非 Parsec 算子的行为。

另外就是我早年读的一些关于 Haskell 的文章应该是写的不太严谨,有很长时间我误以为 >> 是 map ,其实这样理解是有问题的,显然在大多数情况下这两者是完全不同的东西。

猜你喜欢

转载自blog.csdn.net/ccat/article/details/128241434