在 Scala 中为嵌套类编写类型类实例 [英] Writing type class instances for nested classes in Scala

查看:50
本文介绍了在 Scala 中为嵌套类编写类型类实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个最近的堆栈溢出问题,作者想将某种类型的解析器列表更改为返回该类型列表的解析器.我们可以想象用 Scalaz 的 sequence 为 applicative functors 做这件事:

In this recent Stack Overflow question, the author wanted to change a list of parsers of some type into a parser that returns lists of that type. We can imagine doing this with Scalaz's sequence for applicative functors:

import scala.util.parsing.combinator._

import scalaz._
import Scalaz._

object parser extends RegexParsers {
  val parsers = List(1, 2, 3).map(repN(_, """\d+""".r))
  def apply(s: String) = parseAll(parsers.sequence, s)
}

这里我们将返回整数列表的三个解析器的列表转换为返回整数列表列表的解析器.不幸的是,Scalaz 没有为 Parser 提供 Applicative 实例,所以这段代码不能编译,但很容易修复:

Here we take a list of three parsers that return lists of integers and turn it into a parser that returns lists of lists of integers. Unfortunately Scalaz doesn't provide an Applicative instance for Parser, so this code doesn't compile, but that's easy to fix:

import scala.util.parsing.combinator._

import scalaz._
import Scalaz._

object parser extends RegexParsers {
  val parsers = List(1, 2, 3).map(repN(_, """\d+""".r))
  def apply(s: String) = parseAll(parsers.sequence, s)

  implicit def ParserPure: Pure[Parser] = new Pure[Parser] {
    def pure[A](a: => A) = success(a)
  }

  implicit def ParserFunctor: Functor[Parser] = new Functor[Parser] {
    def fmap[A, B](p: Parser[A], f: A => B) = p.map(f)
  }

  implicit def ParserBind: Bind[Parser] = new Bind[Parser] {
    def bind[A, B](p: Parser[A], f: A => Parser[B]) = p.flatMap(f)
  }
}

这按预期工作:parser("1 2 3 4 5 6") 给了我们 List(List(1), List(2, 3), List(4, 5), 6)) ,例如.

This works as expected: parser("1 2 3 4 5 6") gives us List(List(1), List(2, 3), List(4, 5, 6)), for example.

(我知道我可以只给出一个 Apply 实例,但 Bind 实例更简洁.)

(I know I could just give an Apply instance, but the Bind instance is more concise.)

每次扩展 Parsers 时不必这样做会很好,但我不清楚如何为 获取 Applicative 实例Parsers#Parser 更一般.以下天真的方法当然不起作用,因为我们需要 Parsers 的实例是相同的:

It would be nice not to have to do this every time we extend Parsers, but I'm not clear on how to get an Applicative instance for Parsers#Parser more generally. The following naive approach of course doesn't work, since we need the instances of Parsers to be the same:

implicit def ParserBind: Bind[Parsers#Parser] = new Bind[Parsers#Parser] {
  def bind[A, B](p: Parsers#Parser[A], f: A => Parsers#Parser[B]) = p.flatMap(f)
}

我很清楚这应该是可能的,但我对 Scala 的类型系统不够熟悉,不知道如何去做.有什么简单的东西我错过了吗?

It's pretty clear to me that this should be possible, but I'm not comfortable enough with Scala's type system to know how to go about it. Is there something simple that I'm missing?

针对以下答案:我确实尝试了 -Ydependent-method-types 路线,并走到了这一步:

In response to the answers below: I did try the -Ydependent-method-types route, and got this far:

implicit def ParserApplicative(g: Parsers): Applicative[g.Parser] = {
  val f = new Functor[g.Parser] {
    def fmap[A, B](parser: g.Parser[A], f: A => B) = parser.map(f)
  }

  val b = new Bind[g.Parser] {
    def bind[A, B](p: g.Parser[A], f: A => g.Parser[B]) = p.flatMap(f)
  }

  val p = new Pure[g.Parser] {
    def pure[A](a: => A) = g.success(a)
  }

  Applicative.applicative[g.Parser](p, FunctorBindApply[g.Parser](f, b))
}

问题(正如 didierd 指出的那样)是不清楚如何获得 implicit 开始.所以这种方法确实有效,但您必须在语法中添加如下内容:

The problem (as didierd points out) is that it's unclear how to get the implicit to kick in. So this approach does work, but you have to add something like the following to your grammar:

implicit val applicative = ParserApplicative(this)

在这一点上,mixin 方法显然更具吸引力.

At that point the mixin approach is obviously much more attractive.

(作为旁注:我希望能够简单地编写上面的 Applicative.applicative[g.Parser],但这给出了一个错误,说编译器找不到Pure[g.Parser]——即使它就在它旁边.很明显,隐式对依赖方法类型的工作方式有所不同.)

(As a side note: I expected to be able to write simply Applicative.applicative[g.Parser] above, but that gives an error saying the compiler can't find an implicit value for the Pure[g.Parser]—even though one is sitting right next to it. So clearly there's something different about the way implicits work for dependent method types.)

感谢 retronym 指出一个可以实现我在这里想要的东西的技巧.我从他的代码中提取了以下内容:

Thanks to retronym for pointing out a trick that accomplishes what I want here. I've abstracted the following from his code:

implicit def parserMonad[G <: Parsers with Singleton] =
  new Monad[({ type L[T] = G#Parser[T] })#L] {
    def pure[A](a: => A): G#Parser[A] = {
      object dummy extends Parsers
      dummy.success(a).asInstanceOf[G#Parser[A]]
    }

    def bind[A, B](p: G#Parser[A], f: (A) => G#Parser[B]): G#Parser[B] =
      p.flatMap(f)
  }

如果你有这个范围,你会在任何扩展 Parsers 的对象中得到一个 Parser 的 monad 实例.由于演员阵容,这有点作弊,但仍然很整洁.

If you have this in scope, you get a monad instance for Parser in any object extending Parsers. It's kind of cheating because of the cast, but still pretty neat.

推荐答案

我通常在 Parsers

trait BindForParser extends Parsers {
  implicit def ParserBind = new Bind[Parser] {
    def bind[A,B](p: Parser[A], f: A => Parser[B]) = p flatMap f
  }
}

然后你只需要在你的语法(Parsers)中混合它,并且因为Parser实例通常只在Parsers中被操作,有当语法完成并且您不能再混合某些东西之后,之后需要mixin的可能性不大.在您的示例中,您只需执行

Then you just have to mix that in your grammar (Parsers), and as Parser instances are usually manipulated only inside Parsers, there are not much chances the mixin would be needed afterwards, when the grammar is done and you can no longer mix something in. In your example, you just do

object parser extends Parsers with BindForParser

<小时>

关于更普遍的问题,是否可以从外部"做到这一点,最直接的方法可能是像


On the more general question, whether it is possible to do it "from the outside", the most direct way would probably be something like

implicit def ParserBind(grammar: Parsers) = new Bind[grammar.Parser] {
  def bind[A,B](p: grammar.Parser[A], f: A => grammar.Parser[B]) = p flatMap f
}

但这是不允许的,方法参数(此处为 grammar)不被视为稳定标识符,因此 grammar.Parser 不允许作为类型.然而,使用选项 -Xexperimental 是可能的.但即便如此,我也看不出隐式在需要时会如何发挥作用.我们想要的是一个隐式的 Bind[grammar.Parser],而对于语法参数,这不是我们所拥有的.

But this is not allowed, a method parameter (here grammar) is not considered a stable identifier and so grammar.Parser is not allowed as a type. It is however possible with option -Xexperimental. But even then, I don't see how the implicit would kick in when needed. What we want is an implicit Bind[grammar.Parser], and with the grammar parameter this is not what we have.

所以我的回答是它无法完成,但如果有人能想出一些办法,我不会感到惊讶.

So my answer would be it cannot be done, but I would not be that surprised if someone could come up with something.

这篇关于在 Scala 中为嵌套类编写类型类实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆