liveBook · Manning

Many

As explained in Functional Programming in Scala, Second Edition

many is a parser combinator that parses zero or more repetitions of an input parser and returns the parsed values as a list.

many turns a parser for one value into a parser for a list of values. It accepts zero or more consecutive matches: a parser for 'a' can therefore succeed on "aaa" with three characters and on "b" with an empty list. Returning the list keeps repetition general; counting, collecting, or further transformation can be chosen afterward. section 9.2 section 9.8

Count repeated characters

Rather than make repetition return a count, first retain every match and then map the list to its size: char('a').many.map(_.size). This yields 3 for "aaa" and 0 for "b". map changes only a successful result value; it neither examines additional input nor changes success into failure. section 9.2

A list is not always the desired result. slice runs a parser to obtain the matched input portion instead. Thus (char('a') | char('b')).many.slice parses "aaba" as "aaba". For counting repeated 'a' characters, char('a').many.slice.map(_.size) avoids constructing a list merely to discard it. section 9.2.1

many describes optional repetition; many1 describes required repetition. The latter need not be primitive: parse one value, parse many more, then cons the first value onto the remaining list. product and map2 provide the sequencing and result combination needed for this definition. section 9.2.1 section 9.8

extension [A](p: Parser[A]) def many: Parser[List[A]] =
  p.map2(p.many)(_ :: _) | succeed(Nil)

The direct recursive shape is: parse one value and recurse, or succeed with Nil. Its recursive parser argument must be deferred; a strict map2 evaluates p.many too early and can overflow the stack. section 9.2.1 section 9.8

Use many as a building block for related repetition patterns:

  • Define many1 by combining p with p.many and consing the first result onto the list. section 9.8
  • Define listOfN similarly, stopping with Nil when the requested count reaches zero. section 9.8
  • Parse separator-delimited values by parsing one value followed by many occurrences of separator then value. section 9.8

How Functional Programming in Scala, Second Edition covers it

How deep it goes
Derives a recursive implementation, identifies strictness-induced stack overflow, and uses slice to expose an allocation constraint.
Examples
Counts consecutive 'a' characters, slices repeated 'a' or 'b' input, and builds separator-delimited JSON arrays.
What it assumes
Assumes parser values and Scala list mapping, while motivating repetition through concrete character parsers.
What you can do after
Builds zero-or-more, one-or-more, bounded, and separator-delimited repetition parsers from composable operations.
How it connects
Connects repetition to map, slice, many1, product, map2, succeed, defer, and listOfN.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Functional Programming in Scala, Second Edition. The section links point to the parts of the book it draws on.