Функциональное программирование в книге Scala: Как запустить встроенные примеры из главы 9? - PullRequest
0 голосов
/ 26 декабря 2018

Я прорабатываю главу 9 «Функциональное программирование в Scala» (книга Мэннинга Пола Кьюзано и Рунара Бьярнасона).Есть примеры из этой главы, такие как следующие, которые представлены после упражнения 9.1:

char('a').many.slice.map(_.size) ** char('b').many1.slice.map(_.size)

Реализовав методы до этого момента, я не могу запустить этот пример в scala repl.Код можно найти здесь .

Для запуска repl я сделал следующее:

./sbt
> ~exercises/console
scala> import fpinscala.parsing._

При простом запуске char('a') выдается следующая ошибка:

scala> char('a')
<console>:18: error: not found: value char
       char('a')
       ^

Я новичок в Scala, так что, возможно, я что-то пропустил.Должен ли я быть в состоянии запустить методы из черты, как это в repl?Если так, что я пропускаю?В других главах я попытался как можно быстрее повозиться с кодом, чтобы почувствовать концепции и поэкспериментировать с API.Однако в данный момент я не могу запустить самые простые встроенные примеры.

1 Ответ

0 голосов
/ 27 декабря 2018

Параметр типа Parser[+_] оставлен абстрактным почти для всей главы.Только в упражнении 9.12 вы должны попытаться придумать собственную реализацию, а возможное решение предоставляется только в 9.6.2.

До этого у вас есть несколько возможностей, если вы хотите поэкспериментировать с реализациейметодов, которые производят Parser[A] для некоторого типа A:

  • Добавьте их непосредственно к черте Parsers.Там вы можете использовать все другие методы, такие как, например, char.
  • Параметризация вашего кода для всех возможных конструкторов типов Parser[+_], как показано на стр. 158 в 9.4.Раздел начинается с заявления о том, что «У нас еще нет реализации нашей алгебры» , но это не обязательно, поскольку предполагается, что реализация является аргументом, который будет предоставлен позже:

    def jsonParser[Err, Parser[+_]](P: Parsers[Err, Parser]): Parser[JSON] = {
      import P._ // now `char` is available.
      ???
    }
    

    Это работает с вашим кодом:

    def myParser[P[+_]](P: Parsers[P]) = {
      import P._
      char('a').many.slice.map(_.size) **
      char('b').many1.slice.map(_.size)
    }
    
  • В качестве альтернативы, просто увеличьте Parsers еще на одну черту, оставив абстрактную Parser[+_]:

    trait MyParser[Parser[+_]] extends Parsers[Parser] {
      def ab = 
        char('a').many.slice.map(_.size) **
        char('b').many1.slice.map(_.size)
    }
    

Вот ваш собственный код с двумя примерами, которые обязательно компилируются:

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

Обратите внимание, что ParserOps были изменены: у вас были избыточные параметры A и p несколькими способами.

...