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.
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.
Dealing with the path dependent types is pretty tricky. Here's one way:
https://github.com/retronym/scalaz7-experimental/commit/8bf1d2a090cf56d33e11c554e974ea3c82b7b37f
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With