Created
April 15, 2020 21:24
-
-
Save pulasthibandara/f0e9f5238d720d8d9dadf89842526d3f to your computer and use it in GitHub Desktop.
Enables building a coproduct out of a higher kinded type where the type member is type that can be a coproduct.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import shapeless.ops.coproduct.RuntimeInject | |
| import shapeless.{ :+:, CNil, Coproduct, Generic, Inl, Inr } | |
| import scala.language.higherKinds | |
| /** | |
| * Enables building a coproduct out of a higher kinded type where the type member is type | |
| * that can be a coproduct. | |
| * | |
| * for example: | |
| * {{{ | |
| * // Given a type Animal = Dog | Cat | |
| * // it's generic coproduct would be Dog :+: Cat :+: CNil | |
| * scala> sealed trait Animal | |
| * scala> final case class Dog(name: String) | |
| * scala> final case class Cat(age: Int) | |
| * | |
| * // Now we want to get a coproduct to a container that encapsulate animal. say this container is Breed[T]. | |
| * // the coproduct we're interested in would be | |
| * // Breed[Dog] :+: Breed[Cat] :+: CNil. | |
| * | |
| * scala> case class Breed[T](animal: T) | |
| * | |
| * scala> NestedCoproduct.to(Breed(Dog("name")): Breed[Animal])(_.animal) | |
| * res8: Breed[Cat] :+: Breed[Dog] :+: CNil = Inl(Inr(Dog("name"))) | |
| * }}} | |
| * | |
| * | |
| * * @tparam A: The contained type that we want coproducts of | |
| * * @tparam K: The container type of which the coproduct exists | |
| * * @tparam C: The generic coproduct of A | |
| */ | |
| trait NestedCoproduct[A, K[_], C <: Coproduct] { | |
| type Out <: Coproduct | |
| def apply(t: K[A])(by: K[A] => A): Out | |
| } | |
| object NestedCoproduct { | |
| type Aux[A, K[_], C <: Coproduct, O <: Coproduct] = NestedCoproduct[A, K, C] { | |
| type Out = O | |
| } | |
| implicit def caseCNil[A, K[_]]: NestedCoproduct.Aux[A, K, CNil, CNil] = new NestedCoproduct[A, K, CNil] { | |
| type Out = CNil | |
| override def apply(t: K[A])(by: K[A] => A): CNil = | |
| throw new IllegalStateException("Trying to Resolve CNil of a NestedCoproduct") | |
| } | |
| implicit def caseCCons[A, K[_], H, T <: Coproduct, OutT <: Coproduct]( | |
| implicit nestedCoproduct: NestedCoproduct.Aux[A, K, T, OutT], | |
| inj: RuntimeInject[H :+: T], | |
| ): NestedCoproduct.Aux[A, K, H :+: T, K[H] :+: OutT] = new NestedCoproduct[A, K, H :+: T] { | |
| type Out = K[H] :+: OutT | |
| override def apply(t: K[A])(by: K[A] => A): :+:[K[H], OutT] = | |
| Coproduct.runtimeInject[H :+: T](by(t)) match { | |
| case Some(Inl(a)) => Inl[K[H], nestedCoproduct.Out](t.asInstanceOf[K[H]]) | |
| case Some(Inr(a)) => Inr[K[H], nestedCoproduct.Out](nestedCoproduct(t)(by)) | |
| case None => | |
| throw new IllegalStateException(s"The provided ${by(t).getClass} is not a member of coproduct $inj") | |
| } | |
| } | |
| /** A helper to generate the nested coproduct of K[A] */ | |
| def to[A, K[_], C <: Coproduct, Out <: Coproduct](a: K[A])(by: K[A] => A)( | |
| implicit gen: Generic.Aux[A, C], | |
| ngen: NestedCoproduct.Aux[A, K, C, Out] | |
| ) = | |
| ngen.apply(a)(by) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment