Last active
July 16, 2021 05:27
-
-
Save kitlangton/c7fe3f363ed9bfb0e7df07dada2b8263 to your computer and use it in GitHub Desktop.
HListery
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
// HList | |
sealed trait HList | |
object HList { | |
type ::[H, T <: HList] = HCons[H, T] | |
type HNil = HNil.type | |
implicit final class HNilOps(private val self: HNil) extends AnyVal { | |
def ::[A](a: A): A :: HNil = HCons(a, self) | |
} | |
implicit final class HListOps[HL <: HList](private val self: HL) extends AnyVal { | |
def ::[A](a: A): A :: HL = HCons(a, self) | |
} | |
case object HNil extends HList | |
final case class HCons[A, T <: HList](head: A, tail: T) extends HList { self => | |
def get[B](implicit select: Select[B, A :: T]): B = select(self) | |
} | |
} | |
// HList SetUnion (sorta) | |
trait SetUnion[A, B] { | |
type Out <: HList | |
def left(out: Out): A | |
def right(out: Out): B | |
} | |
object SetUnion extends SetUnionLowPri { | |
import HList._ | |
type Aux[A, B, O] = SetUnion[A, B] { type Out = O } | |
implicit def base[B <: HList]: SetUnion.Aux[HNil, B, B] = new SetUnion[HNil, B] { | |
type Out = B | |
override def left(out: B): HNil = HNil | |
override def right(out: B): B = out | |
} | |
implicit def recursiveRemove[H, T <: HList, B <: HList](implicit | |
union: SetUnion[T, B], | |
select: Select[H, B] | |
): Aux[H :: T, B, union.Out] = | |
new SetUnion[H :: T, B] { | |
type Out = union.Out | |
override def left(out: union.Out): H :: T = { | |
val b: B = union.right(out) | |
val h: H = select(b) | |
val t: T = union.left(out) | |
h :: t | |
} | |
override def right(out: union.Out): B = | |
union.right(out) | |
} | |
} | |
trait SetUnionLowPri { | |
import HList._ | |
implicit def recursive[H, T <: HList, B <: HList](implicit | |
union: SetUnion[T, B] | |
): SetUnion.Aux[H :: T, B, H :: union.Out] = | |
new SetUnion[H :: T, B] { | |
type Out = H :: union.Out | |
override def left(out: H :: union.Out): H :: T = { | |
val t: T = union.left(out.tail) | |
out.head :: t | |
} | |
override def right(out: ::[H, union.Out]): B = | |
union.right(out.tail) | |
} | |
} | |
// XIO | |
final case class XIO[-R, +A](run: R => A) | |
object XIO { | |
implicit final class XIOOps[R, A](private val self: XIO[R, A]) extends AnyVal { | |
def zip[R2, B](that: XIO[R2, B])(implicit setUnion: SetUnion[R, R2]): XIO[setUnion.Out, (A, B)] = | |
XIO { (r: setUnion.Out) => | |
val a = self.run(setUnion.left(r)) | |
val b = that.run(setUnion.right(r)) | |
(a, b) | |
} | |
} | |
} | |
// Example | |
object XIOEx extends App { | |
import HList._ | |
val x = XIO[Int :: Double :: HNil, String] { env => | |
s"Int = ${env.get[Int]}, Double = ${env.get[Double]}" | |
} | |
val y = XIO[Boolean :: Double :: HNil, String] { env => | |
s"Boolean = ${env.get[Boolean]}, Double = ${env.get[Double]}" | |
} | |
val xy: XIO[Int :: Boolean :: Double :: HNil, (String, String)] = x zip y | |
println(xy.run(10 :: true :: 12.5 :: HNil)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment