I'm working through chapter 9 of Functional Programming in Scala (the Manning book by Paul Chiusano and Rúnar Bjarnason). There are inline examples from the chapter such as the following which is presented after exercise 9.1:
char('a').many.slice.map(_.size) ** char('b').many1.slice.map(_.size)
Having implemented the methods up to that point I'm unable to run this example in the scala repl. The code can be found here.
I did the following to start the repl:
./sbt
> ~exercises/console
scala> import fpinscala.parsing._
Running simply char('a')
gives me the following error:
scala> char('a')
<console>:18: error: not found: value char
char('a')
^
I'm new to scala so it's possible that I've missed something. Should I be able to run the methods from a trait like this at the repl? If so, what am I missing? In the other chapters I tried to tinker with the code as soon as possible in order to get a feel for concepts and to experiment with the APIs. However I'm unable to run the most simple of inline examples at this point.
The type parameter Parser[+_]
is left abstract for almost the entire chapter. Only in the exercise 9.12 you are supposed to try to come up with your own implementation, and a possible solution is provided only in 9.6.2.
Until then, you have several possibilities if you want to experiment with implementation of methods that produce Parser[A]
for some type A
:
Parsers
trait. There, you can use all other methods, like e.g. char
.Parameterize your code over all possible type constructors Parser[+_]
, as it is shown on page 158 in 9.4. The section starts with the disclaimer that "We don't have an implementation of our algebra yet", but this is not required, because the implementation is assumed to be an argument that will be supplied later:
def jsonParser[Err, Parser[+_]](P: Parsers[Err, Parser]): Parser[JSON] = {
import P._ // now `char` is available.
???
}
This here works with your code:
def myParser[P[+_]](P: Parsers[P]) = {
import P._
char('a').many.slice.map(_.size) **
char('b').many1.slice.map(_.size)
}
Alternatively, just extend Parsers
by yet another trait, still leaving the Parser[+_]
abstract:
trait MyParser[Parser[+_]] extends Parsers[Parser] {
def ab =
char('a').many.slice.map(_.size) **
char('b').many1.slice.map(_.size)
}
Here is your own code, with two examples that definitely compile:
import language.higherKinds
import language.implicitConversions
trait Parsers[Parser[+_]] { self => // so inner classes may call methods of trait
def run[A](p: Parser[A])(input: String): Either[ParseError,A]
implicit def string(s: String): Parser[String]
implicit def operators[A](p: Parser[A]) = ParserOps[A](p)
implicit def asStringParser[A](a: A)(implicit f: A => Parser[String]):
ParserOps[String] = ParserOps(f(a))
def char(c: Char): Parser[Char] =
string(c.toString) map (_.charAt(0))
def or[A](s1: Parser[A], s2: Parser[A]): Parser[A]
def listOfN[A](n: Int, p: Parser[A]): Parser[List[A]]
def many[A](p: Parser[A]): Parser[List[A]]
def slice[A](p: Parser[A]): Parser[String]
def many1[A](p: Parser[A]): Parser[List[A]] =
map2(p, many(p))(_ :: _)
def product[A,B](p: Parser[A], p2: Parser[B]): Parser[(A,B)]
def map[A,B](a: Parser[A])(f: A => B): Parser[B]
def map2[A,B,C](p: Parser[A], p2: Parser[B])(f: (A,B) => C): Parser[C] =
map(product(p, p2))(f.tupled)
def succeed[A](a: A): Parser[A] =
string("") map (_ => a)
case class ParserOps[A](p: Parser[A]) {
def |[B>:A](p2: Parser[B]): Parser[B] = self.or(p,p2)
def or[B>:A](p2: => Parser[B]): Parser[B] = self.or(p,p2)
def many = self.many(p)
def map[B](f: A => B): Parser[B] = self.map(p)(f)
def slice: Parser[String] = self.slice(p)
def many1: Parser[List[A]] = self.many1(p)
def **[B](p2: => Parser[B]): Parser[(A,B)] =
self.product(p,p2)
def product[A,B](p: Parser[A], p2: Parser[B]): Parser[(A,B)] = self.product(p, p2)
def map2[A,B,C](p: Parser[A], p2: Parser[B])(f: (A,B) => C): Parser[C] = self.map2(p, p2)(f)
}
}
case class Location(input: String, offset: Int = 0) {
lazy val line = input.slice(0,offset+1).count(_ == '\n') + 1
lazy val col = input.slice(0,offset+1).reverse.indexOf('\n')
def toError(msg: String): ParseError =
ParseError(List((this, msg)))
def advanceBy(n: Int) = copy(offset = offset+n)
/* Returns the line corresponding to this location */
def currentLine: String =
if (input.length > 1) input.lines.drop(line-1).next
else ""
}
case class ParseError(stack: List[(Location,String)] = List(),
otherFailures: List[ParseError] = List()) {
}
object Parsers {
}
def myParser[P[+_]](P: Parsers[P]) = {
import P._
char('a').many.slice.map(_.size) **
char('b').many1.slice.map(_.size)
}
trait MyParser[P[+_]] extends Parsers[P] {
def ab =
char('a').many.slice.map(_.size) **
char('b').many1.slice.map(_.size)
}
Note that ParserOps
have been modified: you had redundant parameters A
and p
in few methods.