Open vil1 opened 5 years ago
I have a bunch of vague ideas about this issue, I will try to see how far I can get.
Writing down my main idea with the hope someone will be able to help me realising it.
It is about avoiding going through the tuple representation of records and only for interpreting into a covariant target (you have to start somewhere ^^).
lets take an example case class:
final case class Foo(i: Int, s: String, b: Boolean)
and a corresponding schema (isomorphisms eluded for brevity)
val foo = record(
"i" -*>: prim(ScalaInt) :*:
"s" -*>: prim(ScalaString) :*:
"b" -*>: prim(ScalaBool)
)
Which is a tree with this shape :
Record
|
:*:
/ \
Int :*:
/ \
String Boolean
Since Foo
is a case class, we have access to a curried function of type Int => String => Boolean => Foo
:
val curriedCtr: Int => String => Boolean => Foo = (Foo.apply _).curried
Now, imagine we're able to carry some "context" along the traversal of our tree and that this context contains "some F[_]
" where F
is our target functor (and there is an Applicative[F]
instance available).
When reaching the Record
node while going down (the very first step of the recursion), we put F.point(curriedCtr)
in our context (of type F[Int => String => Boolean => Foo]
.
After a few step we reach a first leaf (the Int
node), and the algebra returns an F[Int]
so we can combine it with the F[Int => String => Boolean => Foo]
in our context (using ap
) to obtain an F[String => Boolean => Foo]
that we put back in our context.
We repeat the same mechanism when we reach the String
leaf, using the produced F[String]
to update the context to an F[Boolean => Foo]
.
Finally, when we reach the last leaf and repeat the same operation again, we end up with an F[Foo]
in our context.
Traversing back up the two :*:
nodes is a no-op, and getting back up to the Record
node, we just return the F[Foo]
we have in the context. The important point being that this F[Foo]
never builds nor destructs any tuple to perform its work.
If you've read thus far, you probably agree that this sounds promising.
With an hyloM
(to be implemented) using State
to maintain the so-called context, we should be able to implement the traversal described above (I can detail that claim further if needed).
But the problem is that the type of the said context would have to change during the traversal (from F[Int => String => Boolean => Foo]
to F[Foo]
and everything in between).
Moreover, we must make sure that every time we reach a leaf Schema[A]
, the context is of type F[A => X]
(for some type X
), so a naive approach that would build this context around the existential type F[_]
isn't an option.
So far, I have no idea how to achieve that...
This should be done after #38.
We should search for to avoid going through the "tuple representation" of records in the functors we derive.
Currently, when processing a record like:
The functors we derive from schemas first convert instances of
Foo
to a value of type(Int, (String, Boolean))
; there should be a way of expressing derivations that avoids this unnecessary step.Solving this issue might require to change the type of the
iso
field inUnion
andRecord
, currently anIso[A0, A]
.