简体   繁体   English

如何为 function 类型定义 Monad?

[英]How to define a Monad for a function type?

I am trying Cats for the first time and am using Scala 3, and I am trying to implement a set of parser combinators for self-pedagogy, however;我是第一次尝试 Cats,我正在使用 Scala 3,但是我正在尝试实现一组用于自我教学的解析器组合器; I am stuck on the definition of the tailRecM function for Monad.我停留在 Monad 的tailRecM function 的定义上。 I have managed Functor and Applicative just fine.我已经很好地管理了 Functor 和 Applicative。

I have defined my type in question as a function such that:我已将我的相关类型定义为 function,这样:

type Parser[A] = (input: List[Token]) => ParseResult[A]

with corresponding return types as:相应的返回类型为:

type ParseResult[A] = Success[A] | Failure 
case class Success[A](value: A, tokens: List[Token])
case class Failure(msg: String, tokens: List[Token])

My current definition of tailRecM is as follows:我目前对tailRecM的定义如下:

@annotation.tailrec
def tailRecM[A, B](init: A)(fn: A => Parser[Either[A, B]]): Parser[B] =
  (input: List[Token]) => 
    fn(init)(input) match {
      case f: Failure => f
      case s: Success[Either[A, B]] => s.value match {
        case Right(b) => Success(b, s.tokens)
        case Left(a) => tailRecM(a)(fn) // won't compile 
      }
  }

If I attempt to build I get "Found: Parsing.Parser[B] Required: Parsing.ParseResult[B]" for tailRecM(a)(fn)如果我尝试构建,我会得到"Found: Parsing.Parser[B] Required: Parsing.ParseResult[B]" for tailRecM(a)(fn)

The issue as far as I can tell stems from the fact that my type in question Parser[A] is a function type and not simply a value type?据我所知,这个问题源于我的问题Parser[A]类型是 function 类型而不仅仅是值类型这一事实? I attempted to ameliorate the issue by modifying the tailRecM recursive call to tailRecM(a)(fn)(input) but then this is obviously not stack safe, and also will not compile.我试图通过修改对tailRecM(a)(fn)(input)tailRecM递归调用来改善这个问题,但这显然不是堆栈安全的,也不会编译。

How can I resolve this issue, and more broadly, how can I implement the Monad typeclass for function types in general?我如何解决这个问题,更广泛地说,我如何为 function 类型实现 Monad 类型类?

It's not possible to make tailRecM itself tail-recursive;使tailRecM本身成为尾递归是不可能的; you need to define a tail-recursive helper method您需要定义一个尾递归辅助方法

Here's how the cats library implements tailRecM for Function1以下是cats库如何为Function1实现tailRecM

  def tailRecM[A, B](a: A)(fn: A => T1 => Either[A, B]): T1 => B =
    (t: T1) => {
      @tailrec
      def step(thisA: A): B =
        fn(thisA)(t) match {
          case Right(b)    => b
          case Left(nextA) => step(nextA)
        }
      step(a)
    }

This is because monadic recursion is a form of mutual tail-recursion, where two methods flip back and forth calling each other.这是因为单子递归是相互尾递归的一种形式,其中两个方法来回调用彼此。 The scala compiler can't optimize that. scala 编译器无法对其进行优化。 So instead we inline the definition of the monadic part rather than calling flatMap or another method因此,我们内联 monadic 部分的定义,而不是调用flatMap或其他方法

You need to pass the input again to the tailRecM call您需要再次将input传递给tailRecM调用

tailRecM(a)(fn)(input)

because tailRecM(a)(fn) returns a Parser , but you need the ParserResult from that returned Parser , as you already did in all other cases.因为tailRecM(a)(fn)返回一个Parser ,但是您需要从返回的ParserParserResult ,就像您在所有其他情况下所做的那样。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM