Last active
December 10, 2018 21:10
-
-
Save jooohn/105e09881e4d3118b8bcaa0249ffa003 to your computer and use it in GitHub Desktop.
implicit FizzBuzz
This file contains 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
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