I have a class Foo
that takes a type constructor F
as type parameter:
case class Foo[F[_]](x: F[String])
Now I want to define a member method bar
that is only applicable, if F[T] = Outer[Inner[T]]
for some fixed outer type Outer
, e.g. Option
:
def bar[Inner[_]](implicit ev: ???): Foo[Inner]
The ???
must be something along a natural transformation F ~> Outer·Inner
with ·
being composition of type constructors.
Also how to best write composition of type constructors? I currently write using a type lambda ({type L[X] = Outer[Inner[X]]})#L
.
I don't think there's one defined in ScalaZ, but it's fairly simple to make one, taking scalaz.Leibniz
as a template. I won't focus on composition and convenience methods, and only get the substance:
sealed abstract class LeibnizK[F[_], G[_]] {
def subst[Z[_[_]]](p: Z[F]): Z[G]
}
object LeibnizK {
implicit def refl[F[_]] = new LeibnizK[F, F] {
override def subst[Z[_[_]]](p: Z[F]): Z[F] = p
}
}
Which seems to be the implicit you're looking for:
type Outer[A] = Option[A]
type Id[A] = A
case class Foo[F[_]](me: F[String]) {
// Oh boy, here comes type lambda
def bar[Inner[_]](implicit leibk: LeibnizK[
F,
({type L[A] = Outer[Inner[A]]})#L
]): Outer[Foo[Inner]] = leibk.subst(this).me.map(Foo(_)) // <- OK to use methods of Outer
}
assert(Foo[Option](Some("meh")).bar[Id] == Some(Foo[Id]("meh")))
Check out kind projector compiler plugin. It allows you to write:
λ[A => Outer[Inner[A]]
// or
Lambda[A => Outer[Inner[A]]
instead of
({type L[A] = Outer[Inner[A]]})#L
And for simple cases (no nesting), there's even shorter syntax
(?, Int)
instead of
({type L[A] = (A, Int)})#L