Escrevendo instâncias de classe de tipo para classes aninhadas no Scala

Dentrosta recente pergunta sobre estouro de pilha, o autor queria alterar uma lista de analisadores de algum tipo para um analisador que retorne listas desse tipo. Podemos imaginar fazer isso com o @ Scalsequence para funções de aplicação:

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)
}

Aqui pegamos uma lista de três analisadores que retornam listas de números inteiros e a transformamos em um analisador que retorna listas de listas de números inteiros. Infelizmente, Scalaz não fornece umApplicative instância paraParser, então esse código não é compilado, mas é fácil de corrigir:

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)
  }
}

Isso funciona como esperado:parser("1 2 3 4 5 6") nos dáList(List(1), List(2, 3), List(4, 5, 6)), por exemplo

(Eu sei que eu poderia dar uApply instância, mas oBind instância é mais concisa.)

Seria bom não ter que fazer isso toda vez que estendermosParsers, mas não sei como obter umApplicative instância paraParsers#Parser De forma geral. A seguinte abordagem ingênua, obviamente, não funciona, pois precisamos das instâncias deParsers para ser o mesmo:

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)
}

É bastante claro para mim que isso deve ser possível, mas não estou confortável o suficiente com o sistema de tipos da Scala para saber como fazê-lo. Estou sentindo falta de algo simples?

Em resposta às respostas abaixo: Tentei o-Ydependent-method-types rota, e chegou até aqui:

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))
}

O problema (como didierd aponta) é que não está claro como obter oimplicit para entrar. Portanto, essa abordagem funciona, mas você deve adicionar algo como o seguinte à sua gramática:

implicit val applicative = ParserApplicative(this)

esse momento, a abordagem mixin é obviamente muito mais atraent

(Como observação: eu esperava poder escrever simplesmenteApplicative.applicative[g.Parser] acima, mas isso indica que o compilador não pode encontrar um valor implícito para oPure[g.Parser] - mesmo que alguém esteja sentado ao lado dele. Então, claramente, há algo diferente na maneira como os implícitos funcionam para tipos de métodos dependentes.)

Graças a retronym por apontar um truque que realiza o que quero aqui. Eu abstraí o seguinte dehis 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)
  }

Se você tiver isso no escopo, obterá uma instância de mônad paraParser em qualquer objeto que se estendaParsers. É meio trapaceiro por causa do elenco, mas ainda bem lega

questionAnswers(2)

yourAnswerToTheQuestion