gpt4 book ai didi

scala - 在Scala中为嵌套类编写类型类实例

转载 作者:行者123 更新时间:2023-12-04 04:18:58 25 4
gpt4 key购买 nike

this recent Stack Overflow question中,作者想将某种类型的解析器列表更改为返回该类型列表的解析器。我们可以想象使用Scalaz的sequence来实现适用的仿函数:

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没有为 Applicative提供 Parser实例,因此该代码无法编译,但是很容易解决:
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))

(我知道我只能给出一个 Apply实例,但是 Bind实例更加简洁。)

不必在每次扩展 Parsers时都这样做,这很好,但是我不清楚如何更一般地为 Applicative获取 Parsers#Parser实例。下面的幼稚方法当然是行不通的,因为我们需要 Parsers的实例是相同的:
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的类型系统不够满意,无法知道如何实现。我缺少一些简单的东西吗?

响应以下答案:我确实尝试了 -Ydependent-method-types路由,并走了这么远:
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。因此这种方法确实有效,但是您必须在语法中添加以下内容:
implicit val applicative = ParserApplicative(this)

那时,mixin方法显然更具吸引力。

(作为附带说明:我希望能够在上面简单地编写 Applicative.applicative[g.Parser],但是这会产生一个错误,表明编译器无法找到 Pure[g.Parser]的隐式值,即使它旁边是一个隐含的值。因此很明显有一些东西对于依赖方法类型的隐式工作方式有所不同。)

感谢 retronym指出了可以实现我想要的功能的技巧。我从 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)
}

如果您在范围内拥有此对象,则可以在扩展 Parser的任何对象中获得 Parsers的monad实例。由于类型转换,这有点作弊,但仍然很整洁。

最佳答案

我通常在mixins中为ParserParsers添加隐式扩展

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内部进行操作,因此语法完成后,以后很少需要进行混合,您也无法再在其中混合任何内容了。在您的示例中,您只需要
object parser extends Parsers with BindForParser

关于更普遍的问题,是否可以“从外部”进行,最直接的方法可能是
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],而有了grammar参数,这不是我们所拥有的。

因此,我的回答是这不可能完成,但是如果有人提出一些建议,我不会感到惊讶。

关于scala - 在Scala中为嵌套类编写类型类实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7785762/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com