在Scala中为嵌套类编写类型类实例
在
this recent Stack Overflow question中,作者想要将某种类型的解析器列表更改为返回该类型列表的解析器.我们可以想象使用Scalaz的应用仿函数序列来做到这一点:
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实例,所以这段代码不能编译,但这很容易解决: import scala.util.parsing.combinator._ import scalaz._ import Scalaz._ object parser extends RegexParsers { val parsers = List(1,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,f: A => Parser[B]) = p.flatMap(f) } } 这按预期工作:例如,解析器(“1 2 3 4 5 6”)给出了List(List(1),List(2,3),List(4,5,6)). (我知道我可以给一个Apply实例,但Bind实例更简洁.) 每次我们扩展Parsers时都不必这样做,但我不清楚如何更普遍地获得Parsers#Parser的Applicative实例.以下天真的方法当然不起作用,因为我们需要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 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) } 如果你在范围内有这个,你可以在扩展Parsers的任何对象中获得Parser的monad实例.这是一种作弊,因为演员,但仍然很整洁. 解决方法
我通常在mixins for Parsers中为Parser添加隐式扩展
trait BindForParser extends Parsers { implicit def ParserBind = new Bind[Parser] { def bind[A,f: A => Parser[B]) = p flatMap f } } 然后你只需要在你的语法(Parsers)中混合使用,并且由于Parser实例通常只在Parsers中操作,之后没有太多机会需要mixin,当语法完成并且你不能再混合使用在你的例子中,你就是这么做的 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.Parser作为类型.但是可以选择-Xexperimental.但即便如此,我还是看不到隐含会在需要时如何发挥作用.我们想要的是一个隐式的Bind [grammar.Parser],并且使用语法参数,这不是我们所拥有的. 所以我的回答是无法做到的,但如果有人想出点什么我就不会感到惊讶. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |