Search code examples
scalafunctional-programmingparser-combinators

Functional Programming in Scala book: How to run the inline examples from chapter 9?


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.


Solution

  • 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.