optics-dev / Monocly

Optics experimentation for Dotty
MIT License
32 stars 5 forks source link

Offer various composition modes for Traversal #5

Closed julien-truffaut closed 2 years ago

julien-truffaut commented 4 years ago

Now that we offer custom errors for Traversal, we should also offer ways to compose Traversal:

A typical scenario would be:

sealed trait Foo
case class FooI(i: Int) extends Foo
case class FooS(s: String) extends Foo

val xs: List[Foo] = List(FooI(123), Foos("hello"), FooS("world"))

// permissive:
(each >>> fooI).getAllOrError // Right(List("123"))
// strict:
(each >>> fooI).getAllOrError // Left("FooS(hello) is not a FooI")
// accumulation:
(each >>> fooI).getAllOrError // Left(NonEmptyList("FooS(hello) is not a FooI", "FooS(world) is not a FooI"))
yilinwei commented 4 years ago

Some thoughts on this, it feels like we're using different instances of Monoid for each of the traverses.

  1. permissive is essentially similar to the or, with a last for the error.
  2. strict is essentially an and, with the first for the error
  3. accumulation is essentially an or with concat for the error.

The simplest way (and probably best to limit the API scope) is to have a field on the traverse which abstract over it. Then users can do traverse.withCombine(Combine.strict/accum/permissive).

A final note, is that this helps, because then the returned type can be different as well.