AnOptic
AnOptic is similar to a regular optic, but has an internal different encoding than optic.
Optic_[S, T, A, B]
takes an argument of P[A, B]
, and an implicit instance of some kind of Profunctor[P[_, _]]
and returns P[S, T]
.
AnOptic_[S, T, A, B]
takes a data type shaped like a Profunctor
, that characterizes the construction of the an-optic, and returns
a new instance of the data type with modified types. AnOptic_[S, T, A, B]
like Optic_[S, T, A, B]
is not really used for the encoding
of all an-optic(s)
in Proptics
, and it is only shown for explanation purposes.
For example Iso_[S, T, A, B]
vs AnIso_[S, T, A, B]
An Iso_[S, T, A, B]
is a function P[A, B] => P[S, T]
where's the P[_, _]
is a profunctor.
In order to construct an Iso_[S, T, A, B]
, we need to provide two functions S => A
and B => T
object Iso_ {
def apply[S, T, A, B](view: S => A)(review: B => T): Iso_[S, T, A, B]
}
This is the internal representation of an Iso_[S, T, A, B]
:
abstract class Iso_[S, T, A, B] {
def apply[P[_, _]](pab: P[A, B])(implicit ev: Profunctor[P]): P[S, T]
}
AnIso_[S, T, A, B]
is a function P[A, B] => P[S, T]
where's the P[_, _]
is a data type of Exchange.
The construction mechanism for AnIso_[S, T, A, B]
is the same construction for Iso_[S, T, A, B]
, but the functions are encoded within the Exchange
type.
import proptics.internal.Exchange
abstract class AnIso_[S, T, A, B] {
def apply(exchange: Exchange[A, B, A, B]): Exchange[A, B, S, T]
}
case class Exchange[A, B, S, T](view: S => A, review: B => T)
In order for AnIso_[S, T, A, B]
to be compatible with Iso_[S, T, A, B]
, an instance of Profunctor
of Exchange
has been
introduced.
Profunctor[_, _]
is a type constructor that takes 2 type parameters. Exchange[A, B, S, T]
is a type that has 4 type parameters, so we need
to fix two of the type parameters of Exchange
in order to create an instance of Profunctor
of Exchange
. We can use Scala's type lambda syntax:
implicit def profunctorExchange[E, F]: Profunctor[({ type P[S, T] = Exchange[E, F, S, T] })#P] =
new Profunctor[({ type P[S, T] = Exchange[E, F, S, T] })#P] {
override def dimap[A, B, C, D](fab: Exchange[E, F, A, B])
(f: C => A)
(g: B => D): Exchange[E, F, C, D] =
Exchange(fab.view compose f, g compose fab.review)
}
or we can use the kind projector compiler plugin:
implicit def profunctorExchange[E, F]: Profunctor[Exchange[E, F, *, *]] =
new Profunctor[Exchange[E, F, *, *]] {
override def dimap[A, B, C, D](fab: Exchange[E, F, A, B])
(f: C => A)
(g: B => D): Exchange[E, F, C, D] =
Exchange(fab.view compose f, g compose fab.review)
}
Why does AnOptic exist?
Proptics
is inspired by ideas from purescript-profunctor-lenses
In purescript
we cannot put Optic
directly into a container (e.g. an Option
), therefore a new optic has been designed to have the ability to export
its representation to a data type. Although it is not the case for Scala, still, passing an optic to a function might seems awkward,
therefore this option has been adopted in Proptics
.
The data type representing AnIso_[S, T, A, B]
is the Exchange[A, B, S, T]
.
We can use the toExchange
method of AnIso_[S, T, A, B]
in order to get an Exchange[A, B, S, T]
import proptics.AnIso
// import proptics.AnIso
val anIsoStringToList: AnIso[String, List[Char]] = AnIso[String, List[Char]](_.toList)(_.mkString)
// anIsoStringToList: proptics.AnIso[String,List[Char]] = proptics.AnIso_$$anon$17@74561208
val exchange = anIsoStringToList.toExchange
// exchange: proptics.internal.Exchange[List[Char],List[Char],String,String] =
// Exchange(scala.Function1$$Lambda$9364/0x0000000801a34040@419490d4,
// scala.Function1$$Lambda$9364/0x0000000801a34040@78d86219)
anIsoStringToList.view("Proptics")
// res0: List[Char] = List(P, r, o, p, t, i, c, s)
exchange.review("Proptics".toList)
// res1: String = Proptics
We can later on create a new instance of AnIso
or Iso
from the exchange instance
import proptics.Iso
// import proptics.Iso
val anIsoFromExchange: AnIso[String, List[Char]] =
AnIso[String, List[Char]](exchange.view)(exchange.review)
// anIsoFromExchange: proptics.AnIso[String,List[Char]] = proptics.AnIso_$$anon$17@bf55e9c
val isoFromExchange: Iso[String, List[Char]] = Iso[String, List[Char]](exchange.view)(exchange.review)
// isoFromExchange: proptics.Iso[String,List[Char]] = proptics.Iso_$$anon$16@4c6f5ff7