`slice` changes what a parser returns without changing what it recognizes. If `p` succeeds after consuming input, `p.slice` returns exactly that consumed substring rather than `p`’s parsed value. This is useful when the matched text itself matters more than the values accumulated while matching. section 9.2.1 section 9.3
Count matched characters without keeping a list
A naive parser for `a` characters can use `char('a').many.map(_.size)`, but it constructs a `List[Char]` solely to count it. Write `char('a').many.slice.map(_.size)` instead: `many` recognizes the same run, `slice` returns its input text, and `String.size` supplies the count. For `(char('a') | char('b')).many`, slicing `"aaba"` yields `Right("aaba")`. section 9.2.1
The combinator is not merely a `map` over a completed parser result. Its intended behavior requires a sliced repetition to avoid building the intermediate list that ordinary `many` would produce. That constraint points to `slice` as a primitive with access to parser execution details. section 9.2.1 section 9.8
extension [A](p: Parser[A]) def slice: Parser[String]
The compact extension method exposes `slice` for any parser result type: the original result type is discarded in favor of `String`. section 9.2.1
One direct implementation runs the wrapped parser at the current location. On success, it uses the reported consumed length to take a substring from the original input at the current offset; on failure, it preserves the failure. A more optimized representation can mark parse state as sliced and propagate only lengths through repetition, postponing substring construction until needed. section 9.8 section 9.8
`slice` belongs to the parser algebra alongside `string`, `regex`, `map`, sequencing with `product`, alternatives with `or`, and later `flatMap`. It supplies matched lexical text while those combinators control recognition, transformation, sequencing, and choice. section 9.3 section 9.6
