Skip to content

Tuple semigroupal operations on parsers result in the wider Parser0 type - workarounds? #235

@netvl

Description

@netvl

The original design post contains this quite reasonable statement:

It is interesting to note how Parser1 composes with Parser: if you sequence one of each, you get a Parser1 as a result since once characters are consumed they are never unconsumed.

This is true when you use ~ to compose parsers:

val p1: Parser[A] = ...
val p2: Parser0[B] = ...

val combined1: Parser[(A, B)] = p1 ~ p2
val combined2: Parser[(B, A)] = p2.with1 ~ p1  // can use the with1 workaround if the left parser is Parser0

However, if you use the cats.syntax.contravariantSemigroupal operations to combine multiple parsers into a single tuple, to avoid working with nested tuples and for somewhat better syntax, this fails to work:

import cats.syntax.contravariantSemigroupal._

val p1: Parser[A] = ...
val p2: Parser0[B] = ...

// fails because p2 is Parser0, necessitating the use of Parser0 instances as they are "wider" in a sense,
// therefore `.tupled` returns `Parser0[(A, B)]`
val combined1: Parser[(A, B)] = (p1, p2).tupled

Is there a workaround for this, except using ~? The thing is, multiple ~s in a row result in the parser value being a lot of nested tuples, which doesn't look very nice:

(a ~ b ~ c ~ d ~ e).map {
  case ((((x1, x2), x3), x4), x5) =>
    ...
}

// vs tupled/mapN, if it was possible:

(a, b, c, d, e).mapN {
  (x1, x2, x3, x4, x5) =>
    ...
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions