Skip to content

Instantly share code, notes, and snippets.

@kitlangton
Last active July 16, 2021 05:27
Show Gist options
  • Save kitlangton/c7fe3f363ed9bfb0e7df07dada2b8263 to your computer and use it in GitHub Desktop.
Save kitlangton/c7fe3f363ed9bfb0e7df07dada2b8263 to your computer and use it in GitHub Desktop.
HListery
// 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