Skip to content

Instantly share code, notes, and snippets.

@carymrobbins
Last active April 26, 2018 15:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save carymrobbins/2b07be5ce12d60dfea96cf0bcafc29a3 to your computer and use it in GitHub Desktop.
Save carymrobbins/2b07be5ce12d60dfea96cf0bcafc29a3 to your computer and use it in GitHub Desktop.
Convert a higher-kinded type class into a non-kinded one. While it may be useful for other problems, was created to work around a scalac bug - https://github.com/scala/bug/issues/10849
import cats._
import cats.implicits._
import shapeless._
import shapeless.ops.hlist.{FillWith, ZipWith}
sealed trait UnKind[TC[_[_]], FA] {
type F[_]
type A
final def to(fa: F[A]): FA = fa.asInstanceOf[FA]
final def from(fa: FA): F[A] = fa.asInstanceOf[F[A]]
def value: TC[F]
}
object UnKind {
type Aux[TC[_[_]], FA, F0[_], A0] = UnKind[TC, FA] { type F[a] = F0[a] ; type A = A0 }
def apply[TC[_[_]], FA](implicit ev: UnKind[TC, FA]): Aux[TC, FA, ev.F, ev.A] = ev
implicit def derive[TC[_[_]], F0[_], A0](
implicit ev: TC[F0]
): UnKind.Aux[TC, F0[A0], F0, A0] =
new UnKind[TC, F0[A0]] {
override type F[a] = F0[a]
override type A = A0
override def value: TC[F0] = ev
}
implicit def value[TC[_[_]], FA](x: UnKind[TC, FA]): TC[x.F] = x.value
}
object Example {
def main(args: Array[String]): Unit = {
println(Monoid[Info].empty)
println(Monoid[Info].combine(Info(Some("foo")), Info(Some("bar"), Some("baz"))))
}
}
final case class Info(
name: Option[String] = None,
city: Option[String] = None,
friendIds: Vector[Int] = Vector.empty
)
object Info {
implicit val monoid: Monoid[Info] = GMerge[Info].monoid
}
trait GMerge[A] {
def monoid: Monoid[A]
}
object GMerge {
def apply[A: GMerge]: GMerge[A] = implicitly
implicit def default[A, L <: HList](
implicit g: Generic.Aux[A, L],
z: ZipWith.Aux[L, L, polyMerge.type, L],
fw: FillWith[polyEmpty.type, L]
): GMerge[A] = new GMerge[A] {
override val monoid: Monoid[A] = new Monoid[A] {
override def combine(x: A, y: A): A = g.from(g.to(x).zipWith(g.to(y))(polyMerge))
override def empty: A = g.from(fw.apply)
}
}
object polyMerge extends Poly2 {
implicit def cases[A](implicit F: UnKind[MonoidK, A]): Case.Aux[A, A, A] =
at[A, A]((x, y) => F.to(F.combineK(F.from(x), F.from(y))))
}
object polyEmpty extends Poly0 {
implicit def cases[A](implicit F: UnKind[MonoidK, A]): Case0[A] = at[A](F.to(F.empty[F.A]))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment