Skip to content

Commit

Permalink
implicits page done
Browse files Browse the repository at this point in the history
  • Loading branch information
j-mie6 committed Dec 27, 2023
1 parent ab3a1db commit 86c4824
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 2 deletions.
2 changes: 1 addition & 1 deletion docs/api-guide/directory.conf
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ laika.navigationOrder = [
character.md
combinator.md # TODO:
generic.md
implicits.md # TODO:
syntax.md
position.md
registers.md
expr
Expand Down
86 changes: 85 additions & 1 deletion docs/api-guide/syntax.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,89 @@
{%
laika.title = "`parsley.implicits`"
laika.excludeFromNavigation = true
%}
# Synactic Extensions (`parsley.implicits`)
The `parsley.implicits` package contains several modules that enable new "syntax"
on parsers or other values. There are currently four such modules:

* `parsley.implicits.character`: contains conversions that allow for character
and string literals to serve as parsers.
* `parsley.implicits.combinator`: contains an implicit function that allows any
parser to drop its result when required by the type of another combinator.
* `parsley.implicits.lift`: enables the `lift` method on functions to allow them
to work on parsers.
* `parsley.implicits.zipped`: enables the `zipped` method on tuples of parsers to
sequence and combine their results with a single function.

## Implicit Conversions
The `charLift` and `stringLift` conversions in `parsley.implicits.character`
allow for Scala character and string literals to work directly as parsers for
those specific literals. For example:

```scala mdoc:to-string
import parsley.implicits.character._

val p = 'a' ~> "bc"
p.parse("abc")
p.parse("axy")
```

In the above, `'a': Parsley[Char]`, and `"bc": Parsley[String]`.

@:callout(error)
If you see an error like this, when you otherwise have the implicit imported:

```scala mdoc:nest:invisible
import parsley.token.Lexer
import parsley.token.descriptions.LexicalDesc
val lexer = new Lexer(LexicalDesc.plain)

import lexer.lexeme.symbol.implicits._
```

```scala mdoc:fail
val p = "cb" <~ 'a'
p.parse("cba")
```

Then this likely means that you have *another* conversion in scope and the
ambiguity is not resolved. If the arguments reversed, this will become more
evident:

```scala mdoc:fail
val p = 'a' ~> "bc"
p.parse("abc")
```

In this case, a `lexer.lexeme.symbol.implicits` is imported and is clashing.
@:@

## Improved Sequencing
Both the `lift` and `zipped` modules within `parsley.implicits` enable new
ways of sequencing parsers in an idiomatic way. The `lift` syntax is perhaps
more natural, where the function to apply appears to the left of the arguments:

```scala mdoc:to-string
import parsley.character.char
import parsley.implicits.lift._

val add = (x: Int, y: Int) => x + y
add.lift(char('a').as(5), char('b').as(6)).parse("ab")
```

However, while `lift` works well when the function has its type fully elaborated,
it does not infer well:

```scala mdoc:fail
(_ + _).lift(char('a').as(5), char('b').as(6)).parse("ab")
```

This is where `zipped` comes in: by placing the function to the right of its
arguments, it can infer the type of the function based on the arguments. This
may appear slightly less natural, however:

```scala mdoc:to-string
import parsley.implicits.zipped._
(char('a').as(5), char('b').as(6)).zipped(_ + _).parse("ab")
```

Both `lift` and `zipped` work for up to 22-argument functions.

0 comments on commit 86c4824

Please sign in to comment.