Context functions and type aliases allow us to express dependency on context as a result type as opposed to a function parameter. This has some very nice properties, but it begins to fall apart in certain situations. To start from the beginning, here’s an example of a context type:
type Executee[A] = ExecutionContext ?=> A
val executee: Executee[Future[Int]] = Future("3").map(_.toInt)
You will notice that the context type (I’m tending have these end with the -ee suffix, indicating receiver of execution in this case) is a higher kinded type than the original context input in order to allow it to describe many types of outputs. This hits a snag when the input context is higher kinded too:
type Decodee[A,B] = Decoder[A] ?=> B
type Encodee[A, B] = Encoder[A] ?=> B
val idem: Encodee[String, Decodee[String, String]] = decode[String]("hello world".asJson).getOrElse(???)
I am trying to brainstorm a better way to stack these context types on each other. What I’ve come up with so far is a bit ugly, and I was wondering if someone could find a better way:
type Decodee[F[_]] = [B] =>> Decoder[B] ?=> F[B]
type Encodee[F[_]] = [B] =>> Encoder[B] ?=> F[B]
type Res[A] = [B] =>> A
val idem: Encodee[Decodee[Res[Int]]][String] = decode[String]("5".asJson).getOrElse(???).toInt
This allows for an input type that is passed in outside the stacked type (String
in this case) which is passed through the context dependency chain, and a result type. It still looks unwieldy to me though. Any thoughts?