Definition of Transformer.Fallible
and Mode
object Transformer {
trait Fallible[F[+x], Source, Dest] {
def transform(value: Source): F[Dest]
}
}
So a Fallible
transformer takes a Source
and gives back a Dest
wrapped in an F
where F
is the wrapper type for our transformations eg. if F[+x]
= Either[List[String], x]
then the transform
method will return an Either[List[String], Dest]
.
sealed trait Mode[F[+x]] {
def pure[A](value: A): F[A]
def map[A, B](fa: F[A], f: A => B): F[B]
def traverseCollection[A, B, AColl <: Iterable[A], BColl <: Iterable[B]](
collection: AColl,
transformation: A => F[B]
)(using factory: Factory[B, BColl]): F[BColl]
}
Moving on to Mode
, what exactly is it and why do we need it? So a Mode[F]
is typeclass that gives us two bits of information:
- a hint for the derivation mechanism which transformation mode to use (hence the name!)
- some operations on the abstract
F
wrapper type, namely:
pure
is for wrapping arbitrary values intoF
, eg. ifF[+x] = Either[List[String], x]
then callingpure
would involve just wrapping the value in aRight.apply
call.
map
is for operating on the wrapped values, eg. if we find ourselves with aF[Int]
in hand and we want to transform the value 'inside' to aString
we can call.map(_.toString)
to yield aF[String]
traverseCollection
is for the cases where we end up with a collection of wrapped values (eg. aList[F[String]]
) and we want to transform that into aF[List[String]]
according to the rules of theF
type wrapper and not blow up the stack in the process
As mentioned earlier, Modes
come in two flavors - one for error accumulating transformations (Mode.Accumulating[F]
) and one for fail fast transformations (Mode.FailFast[F]
):
object Mode {
trait Accumulating[F[+x]] extends Mode[F] {
def product[A, B](fa: F[A], fb: F[B]): F[(A, B)]
}
trait FailFast[F[+x]] extends Mode[F] {
def flatMap[A, B](fa: F[A], f: A => F[B]): F[B]
}
}
Each one of these exposes one operation that dictates its approach to errors, flatMap
entails a dependency between fallible transformations so if we chain multiple flatMaps
together our transformation will stop at the very first error - contrary to this, Mode.Accumulating
exposes a product
operation that given two independent transformations wrapped in an F
gives us back a tuple wrapped in an F
. What that really means is that each transformation is independent from one another so we're able to accumulate all of the errors produced by these.
For accumulating transformations ducktape
provides instances for Either
with any subtype of Iterable
on the left side, so that eg. Mode.Accumulating[[A] =>> Either[List[String], A]]
is available out of the box (under the subclass of Mode.Accumulating.Either[String, List]
).
For fail fast transformations, instances for Option
(Mode.FailFast.Option
) and Either
(Mode.FailFast.Either
) are avaiable out of the box.