Consider the following type definition:
trait LiftF[F[_], G[_]] {
def liftF[A](fa: F[A]): G[A]
}
When providing a requirement for an implicit of this type in context bounds (using kind projector plugin) we have to write it like this:
def func[A, G[_], F[_]: LiftF[?[_], G]](a: F[A]): G[A]
I would like to get rid of the ?[_]
part, so my initial guess was to write a type To[G[_]]
that returns LiftF[?[_], G]
in order to transform the above function definition into
def func[A, G[_], F[_]: LiftF.To[G]](a: F[A]): G[A]
However, when writing type To
definition as
type To[G[_]] = LiftF[?[_], G]
I get the following compilation error:
Error:(17, 20) type Λ$ takes type parameters
type To[G[_]] = LiftF[?[_], G]
Trying to rewrite it with existential types produces the following type definition:
type To[G[_]] = LiftF[F, G] forSome { type F[X] }
This compiles fine but, unsuprisingly, can't be applied to other type parameters, so desired function definition can't be achieved.
I managed to implement the "partial application" part with code inspired by the aux pattern:
trait To[G[_]] {
type From[F[_]] = LiftF[F, G]
}
Sadly enough, this leaves me with syntax that is arguably worse than the original one:
def func[A, G[_], F[_]: LiftF.To[G]#From](a: F[A]): G[A]
My question is - can I achieve the originally proposed syntax in Scala with the help of kind projector or I should just stick with ?[_]
?