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 ?[_]?
As far as I understand it, the kind-projector can't really help you here:
would simply be mechanically rewritten into something like
but it's invalid in 2.12.x, because it expects a plain type of kind
*on the right hand side of the definition.If you move the parameter
Fto the left hand side, you end up withwhich you then have to use as
To[G, ?[_]], which obviously doesn't buy you anything either, it simply swaps the order of arguments. Therefore I'd suggest to just useLiftF[?[_], G]and take solace from the fact that you don't have to write out({ type L[F[_]] = LiftF[F, G] })#Lexplicitly.By the way, in Dotty, this works just fine: