Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing type class instances for nested classes in Scala

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

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

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.

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

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

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?


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

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)

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

(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.)


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

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.

like image 924
Travis Brown Avatar asked Oct 16 '11 16:10

Travis Brown


2 Answers

I usually add implicit extension to Parser in mixins for 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
  }
}

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
}

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.

like image 81
Didier Dupont Avatar answered Oct 19 '22 09:10

Didier Dupont


Dealing with the path dependent types is pretty tricky. Here's one way:

https://github.com/retronym/scalaz7-experimental/commit/8bf1d2a090cf56d33e11c554e974ea3c82b7b37f

like image 24
retronym Avatar answered Oct 19 '22 09:10

retronym