Skip to content

Instantly share code, notes, and snippets.

@SystemFw
Last active October 29, 2020 21:47
Show Gist options
  • Save SystemFw/1038a0ba297760efca946bbe5c1650bd to your computer and use it in GitHub Desktop.
Save SystemFw/1038a0ba297760efca946bbe5c1650bd to your computer and use it in GitHub Desktop.

Revisions

  1. SystemFw revised this gist Oct 29, 2020. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions Example.scala
    Original file line number Diff line number Diff line change
    @@ -10,13 +10,13 @@ import shapeless._

    trait Check[T] {
    type Id
    def register[A <: T](t: T): Id
    def register(t: T): Id
    def check(registered: Set[Id]): Boolean
    }
    object Check {
    def apply[T](implicit ev: Checks[T]): Check[T] = new Check[T] {
    type Id = Int
    def register[A <: T](t: T): Id = ev.checks.indexWhere(_.apply(t))
    def register(t: T): Id = ev.checks.indexWhere(_.apply(t))
    def check(registered: Set[Id]): Boolean =
    ev.checks.indices.forall(registered.contains)
    }
  2. SystemFw revised this gist Oct 28, 2020. No changes.
  3. SystemFw revised this gist Oct 28, 2020. 1 changed file with 44 additions and 63 deletions.
    107 changes: 44 additions & 63 deletions Example.scala
    Original file line number Diff line number Diff line change
    @@ -1,70 +1,48 @@
    // Stemming from
    // stemming from
    // https://gitter.im/typelevel/general?at=5f987f8161007f7d1b9b4b92
    // The idea is
    // read the JSON files in a directory
    // try to decode each of them as Event which could be either One or Two, we don't know this at compile time
    // serialize the values we got, again could be either One or Two and compare it with the content of the JSON file
    // verify that there was at least a JSON file for One and Two (exhaustivity check)

    object Lib {
    import shapeless._
    import shapeless.ops.coproduct._
    import cats.syntax.all._
    import shapeless._

    def check[A] = new CheckBuilder[A]

    class CheckBuilder[A] {
    def apply[R <: Coproduct](
    names: Set[String]
    )(implicit ev: Generic.Aux[A, R], c: Check[R]) =
    c(names)
    trait Check[T] {
    type Id
    def register[A <: T](t: T): Id
    def check(registered: Set[Id]): Boolean
    }
    object Check {
    def apply[T](implicit ev: Checks[T]): Check[T] = new Check[T] {
    type Id = Int
    def register[A <: T](t: T): Id = ev.checks.indexWhere(_.apply(t))
    def check(registered: Set[Id]): Boolean =
    ev.checks.indices.forall(registered.contains)
    }
    def getName[A, R <: Coproduct](
    a: A
    )(implicit ev: Generic.Aux[A, R], n: GetName[R]): Option[String] = n(a)

    trait GetName[C <: Coproduct] extends Serializable {
    def apply(x: Any): Option[String]
    }
    object GetName extends GetNameLow {
    implicit def base[H](implicit castH: Typeable[H]): GetName[H :+: CNil] =
    new GetName[H :+: CNil] {
    def apply(x: Any): Option[String] =
    castH.cast(x).as(castH.describe)
    }
    }
    trait GetNameLow {
    implicit def inductive[H, T <: Coproduct](
    implicit next: GetName[T],
    castH: Typeable[H]
    ): GetName[H :+: T] =
    new GetName[H :+: T] {
    def apply(x: Any): Option[String] =
    castH
    .cast(x)
    .as(castH.describe)
    .orElse(next(x))
    }
    trait Checks[T] {
    def checks: Vector[Any => Boolean]
    }

    trait Check[T] {
    def apply(s: Set[String]): Boolean
    }
    object Check extends CheckLow {
    implicit def base: Check[CNil] =
    new Check[CNil] {
    def apply(names: Set[String]): Boolean = true
    }
    }
    trait CheckLow {
    implicit def inductive[H, T <: Coproduct](
    implicit next: Check[T],
    castH: Typeable[H]
    ): Check[H :+: T] =
    new Check[H :+: T] {
    def apply(names: Set[String]): Boolean =
    names.contains(castH.describe) && next(names)
    }
    implicit def base: Checks[CNil] =
    new Checks[CNil] {
    def checks = Vector.empty
    }

    implicit def inductive[H, T <: Coproduct](
    implicit next: Checks[T],
    castH: Typeable[H]
    ): Checks[H :+: T] =
    new Checks[H :+: T] {
    def checks = (castH.cast(_: Any).isDefined) +: next.checks
    }

    implicit def generic[T, R <: Coproduct](
    implicit ev: Generic.Aux[T, R],
    check: Checks[R]
    ): Checks[T] = new Checks[T] {
    def checks = check.checks
    }
    }

    @@ -78,25 +56,28 @@ object Ex {
    }

    object Exhaustive {
    val checks: collection.mutable.Set[String] = collection.mutable.Set()
    val check = Check[Ex.A]

    val checks: collection.mutable.Set[check.Id] = collection.mutable.Set()

    Lib.getName(Ex.b).foreach(checks.add)
    Lib.getName(Ex.c).foreach(checks.add)
    checks.add(check.register(Ex.b))
    checks.add(check.register(Ex.c))

    val result = Lib.check[Ex.A](checks.toSet)
    val result = check.check(checks.toSet)
    }

    object NonExhaustive {
    val checks: collection.mutable.Set[String] = collection.mutable.Set()
    val check = Check[Ex.A]

    Lib.getName(Ex.b).foreach(checks.add)
    val checks: collection.mutable.Set[check.Id] = collection.mutable.Set()

    val result = Lib.check[Ex.A](checks.toSet)
    checks.add(check.register(Ex.b))

    val result = check.check(checks.toSet)
    }

    // scala> Exhaustive.result
    // res0: Boolean = true

    // scala> NonExhaustive.result
    // res1: Boolean = false

  4. SystemFw created this gist Oct 27, 2020.
    102 changes: 102 additions & 0 deletions Example.scala
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,102 @@
    // Stemming from
    // https://gitter.im/typelevel/general?at=5f987f8161007f7d1b9b4b92
    // The idea is
    // read the JSON files in a directory
    // try to decode each of them as Event which could be either One or Two, we don't know this at compile time
    // serialize the values we got, again could be either One or Two and compare it with the content of the JSON file
    // verify that there was at least a JSON file for One and Two (exhaustivity check)

    object Lib {
    import shapeless._
    import shapeless.ops.coproduct._
    import cats.syntax.all._

    def check[A] = new CheckBuilder[A]

    class CheckBuilder[A] {
    def apply[R <: Coproduct](
    names: Set[String]
    )(implicit ev: Generic.Aux[A, R], c: Check[R]) =
    c(names)
    }
    def getName[A, R <: Coproduct](
    a: A
    )(implicit ev: Generic.Aux[A, R], n: GetName[R]): Option[String] = n(a)

    trait GetName[C <: Coproduct] extends Serializable {
    def apply(x: Any): Option[String]
    }
    object GetName extends GetNameLow {
    implicit def base[H](implicit castH: Typeable[H]): GetName[H :+: CNil] =
    new GetName[H :+: CNil] {
    def apply(x: Any): Option[String] =
    castH.cast(x).as(castH.describe)
    }
    }
    trait GetNameLow {
    implicit def inductive[H, T <: Coproduct](
    implicit next: GetName[T],
    castH: Typeable[H]
    ): GetName[H :+: T] =
    new GetName[H :+: T] {
    def apply(x: Any): Option[String] =
    castH
    .cast(x)
    .as(castH.describe)
    .orElse(next(x))
    }
    }

    trait Check[T] {
    def apply(s: Set[String]): Boolean
    }
    object Check extends CheckLow {
    implicit def base: Check[CNil] =
    new Check[CNil] {
    def apply(names: Set[String]): Boolean = true
    }
    }
    trait CheckLow {
    implicit def inductive[H, T <: Coproduct](
    implicit next: Check[T],
    castH: Typeable[H]
    ): Check[H :+: T] =
    new Check[H :+: T] {
    def apply(names: Set[String]): Boolean =
    names.contains(castH.describe) && next(names)
    }
    }
    }

    object Ex {
    sealed trait A
    case class B() extends A
    case class C() extends A

    def b: A = B()
    def c: A = C()
    }

    object Exhaustive {
    val checks: collection.mutable.Set[String] = collection.mutable.Set()

    Lib.getName(Ex.b).foreach(checks.add)
    Lib.getName(Ex.c).foreach(checks.add)

    val result = Lib.check[Ex.A](checks.toSet)
    }

    object NonExhaustive {
    val checks: collection.mutable.Set[String] = collection.mutable.Set()

    Lib.getName(Ex.b).foreach(checks.add)

    val result = Lib.check[Ex.A](checks.toSet)
    }

    // scala> Exhaustive.result
    // res0: Boolean = true

    // scala> NonExhaustive.result
    // res1: Boolean = false