1
votes

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.

1
Don't they have complete stand-alone runnable code samples on their github? Moreover, not quite clear why you're trying to run anything in the repl at all. Is interactivity of any value in this situation? Why not simply write all the examples as ordinary code, together with the parsing framework? - Andrey Tyukin
Not exactly. The provide exercise templates and an answer key but there is nothing that is stand-alone or pre-packaged to be runnable. In my case, I'm trying to run the code that is inline from chapter 9 (from the trait) but I'm unable to figure out how to do that as I go. - Damon Snyder
Well, importing fpinscala.parsing._ does not bring any method called. char into scope. You would have to create a new actual parser that extends Parsers, and then use the inherited methods, e.g. char. - Andrey Tyukin

1 Answers

2
votes

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:

  • Add them directly to the 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.