Skip to content

Instantly share code, notes, and snippets.

@jooohn
Last active Dec 10, 2018
Embed
What would you like to do?
implicit FizzBuzz
sealed trait Nat
trait Zero extends Nat
trait Succ[N <: Nat] extends Nat
type Nat1 = Succ[Zero]
type Nat2 = Succ[Nat1]
type Nat3 = Succ[Nat2]
type Nat4 = Succ[Nat3]
type Nat5 = Succ[Nat4]
sealed trait ToInt[N <: Nat] { def value: Int }
object ToInt {
def apply[N <: Nat](implicit T: ToInt[N]): ToInt[N] = T
implicit val zeroToInt: ToInt[Zero] = new ToInt[Zero] { def value: Int = 0 }
implicit def succToInt[N <: Nat](implicit T: ToInt[N]): ToInt[Succ[N]] =
new ToInt[Succ[N]] { def value: Int = T.value + 1 }
}
sealed trait Sum[A <: Nat, B <: Nat] { type Out <: Nat }
object Sum {
def apply[A <: Nat, B <: Nat](implicit S: Sum[A, B]): Aux[A, B, S.Out] = S
type Aux[A <: Nat, B <: Nat, C <: Nat] = Sum[A, B] { type Out = C }
implicit def sumZero[B <: Nat]: Aux[Zero, B, B] = new Sum[Zero, B] { type Out = B }
implicit def sum[A <: Nat, B <: Nat, C <: Nat](implicit S: Aux[A, Succ[B], C]): Aux[Succ[A], B, C] = new Sum[Succ[A], B] { type Out = C }
}
sealed trait Diff[A <: Nat, B <: Nat] { type Out <: Nat }
object Diff {
def apply[A <: Nat, B <: Nat](implicit D: Diff[A, B]): Aux[A, B, D.Out] = D
type Aux[A <: Nat, B <: Nat, C <: Nat] = Diff[A, B] { type Out = C }
implicit def diffZero[A <: Nat]: Aux[A, Zero, A] = new Diff[A, Zero] { type Out = A }
implicit def diff[A <: Nat, B <: Nat, C <: Nat](implicit D: Aux[A, B, C]): Aux[Succ[A], Succ[B], C] = new Diff[Succ[A], Succ[B]] { type Out = C }
}
sealed trait Divisible[D <: Nat, N <: Nat]
object Divisible {
def apply[D <: Nat, N <: Nat](implicit D: Divisible[D, N]): Divisible[D, N] = D
implicit def zeroDiv[D <: Succ[_]]: Divisible[D, Zero] = new Divisible[D, Zero] {}
implicit def succDiv[D <: Succ[_], N <: Nat, Diff <: Nat](implicit diff: Diff.Aux[N, D, Diff], D: Divisible[D, Diff]): Divisible[D, N] =
new Divisible[D, N] {}
}
sealed trait And[A, B]
object And {
implicit def and[A, B](implicit ev1: A, ev2: B): And[A ,B] = new And[A, B] {}
}
sealed trait FizzBuzz[N <: Nat] {
def out: String
println(out)
}
trait OtherNat {
implicit def otherwise[N <: Nat](implicit T: ToInt[N]): FizzBuzz[N] =
new FizzBuzz[N] { override def out = s"${T.value}" }
}
trait IfDivisible3Or5 extends OtherNat {
implicit def divisible3[N <: Nat](implicit D: Divisible[Nat3, N]): FizzBuzz[N] =
new FizzBuzz[N] { override def out: String = "Fizz" }
implicit def divisible5[N <: Nat](implicit D: Divisible[Nat5, N]): FizzBuzz[N] =
new FizzBuzz[N] { override def out: String = "Buzz" }
}
object FizzBuzz extends IfDivisible3Or5 {
def apply[N <: Nat](implicit F: FizzBuzz[N]): FizzBuzz[N] = F
implicit def divisible3And5[N <: Nat](implicit A: Divisible[Nat3, N] And Divisible[Nat5, N]): FizzBuzz[N] =
new FizzBuzz[N] { override def out: String = "FizzBuzz" }
}
sealed trait Prod[A <: Nat, B <: Nat] {
type Out <: Nat
}
object Prod {
def apply[A <: Nat, B <: Nat](implicit P: Prod[A, B]): Aux[A, B, P.Out] = P
type Aux[A <: Nat, B <: Nat, C <: Nat] = Prod[A, B] { type Out = C }
implicit def prod1[B <: Nat]: Aux[Zero, B, Zero] = new Prod[Zero, B] { type Out = Zero }
implicit def prod2[A <: Nat, B <: Nat, C <: Nat, D <: Nat](implicit P: Prod.Aux[A, B, C], sum: Sum.Aux[B, C, D]): Aux[Succ[A], B, D] =
new Prod[Succ[A], B] { type Out = D }
}
sealed trait Range[E[_ <: Nat], A <: Nat, B <: Nat]
object Range {
def apply[E[_ <: Nat], A <: Nat, B <: Nat](implicit R: Range[E, A, B]): Range[E, A, B] = R
implicit def equal[E[_ <: Nat], N <: Nat](implicit E: E[N]): Range[E, N, N] = new Range[E, N, N] {}
implicit def range[E[_ <: Nat], A <: Nat, B <: Nat](implicit R: Range[E, A, B], E: E[Succ[B]]): Range[E, A, Succ[B]] = new Range[E, A, Succ[B]] {}
}
val nat10 = Prod[Nat2, Nat5]
type Nat10 = nat10.Out
val nat100 = Prod[Nat10, Nat10]
type Nat100 = nat100.Out
Range[FizzBuzz, Nat1, Nat100]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment