Skip to content

Instantly share code, notes, and snippets.

@ceedubs
Last active August 29, 2015 13:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ceedubs/10122016 to your computer and use it in GitHub Desktop.
Save ceedubs/10122016 to your computer and use it in GitHub Desktop.
Type level FizzBuzz
package typelevelfizzbuzz
import Nat._
sealed trait Nat
object Nat {
sealed trait _0 extends Nat
sealed trait Succ[T <: Nat] extends Nat
type _1 = Succ[_0]
type _2 = Succ[_1]
type _3 = Succ[_2]
type _4 = Succ[_3]
type _5 = Succ[_4]
type _20 = Succ[Succ[Succ[Succ[Succ[Succ[Succ[Succ[Succ[Succ[Succ[Succ[Succ[Succ[Succ[_5]]]]]]]]]]]]]]]
}
trait FizzBuzzResult[N <: Nat]
/**
* Implicit results organized type class hierarchy with higher priority results
* in subclasses so compiler prioritizes them
*/
sealed trait FizzBuzzResultInstances0 {
def result[N <: Nat](s: String): FizzBuzzResult[N] = new FizzBuzzResult[N] {
override def toString = s
}
implicit def succResult[N <: Nat](implicit r: FizzBuzzResult[N], n: NatToInt[Succ[N]]): FizzBuzzResult[Succ[N]] =
result[Succ[N]](s"$r\n${n.toInt}")
}
sealed trait FizzBuzzResultInstances1 extends FizzBuzzResultInstances0 {
implicit def fizzResult[N <: Nat](implicit d3: Div[Succ[N], _3], r: FizzBuzzResult[N]): FizzBuzzResult[Succ[N]] =
result[Succ[N]](s"$r\nfizz")
implicit def buzzResult[N <: Nat](implicit d5: Div[Succ[N], _5], r: FizzBuzzResult[N]): FizzBuzzResult[Succ[N]] =
result[Succ[N]](s"$r\nbuzz")
}
sealed trait FizzBuzzResultInstances2 extends FizzBuzzResultInstances1 {
implicit def oneResult: FizzBuzzResult[_1] =
result[_1](NatToInt[_1].toInt.toString)
implicit def fizzBuzzResult[N <: Nat](implicit d3: Div[Succ[N], _3], d5: Div[Succ[N], _5], r: FizzBuzzResult[N]): FizzBuzzResult[Succ[N]] =
result[Succ[N]](s"$r\nfizzbuzz")
}
object FizzBuzzResult extends FizzBuzzResultInstances2 {
def apply[N <: Nat](implicit f: FizzBuzzResult[N]) = f
}
/** Evidence N is evenly divisible by M */
trait Div[N <: Nat, M <: Nat]
object Div {
def div[N <: Nat, M <: Nat]: Div[N, M] = new Div[N, M] {}
// I don't actually use this but I feel like it should be defined.
implicit def div1[N <: Nat]: Div[N, _1] = div[N, _1]
implicit def divSelf[N <: Nat]: Div[N, N] = div[N, N]
implicit def succDiv3[N <: Nat](implicit d: Div[N, _3]): Div[Succ[Succ[Succ[N]]], _3] =
div[Succ[Succ[Succ[N]]], _3]
implicit def succDiv5[N <: Nat](implicit d: Div[N, _5]): Div[Succ[Succ[Succ[Succ[Succ[N]]]]], _5] =
div[Succ[Succ[Succ[Succ[Succ[N]]]]], _5]
}
trait NatToInt[N <: Nat] {
def toInt: Int
}
object NatToInt {
def apply[N <: Nat](implicit n: NatToInt[N]) = n
def natToInt[N <: Nat](int: Int): NatToInt[N] = new NatToInt[N] {
val toInt = int
}
implicit val zeroToInt: NatToInt[_0] = natToInt[_0](0)
implicit def succToInt[N <: Nat](implicit tToInt: NatToInt[N]): NatToInt[Succ[N]] =
natToInt[Succ[N]](1 + tToInt.toInt)
}
/* Console results:
scala> import typelevelfizzbuzz._, typelevelfizzbuzz.Nat._20
import typelevelfizzbuzz._
import typelevelfizzbuzz.Nat._20
scala> FizzBuzzResult[_20]
res0: typelevelfizzbuzz.FizzBuzzResult[typelevelfizzbuzz.Nat._20] =
1
2
fizz
4
buzz
fizz
7
8
fizz
buzz
11
fizz
13
14
fizzbuzz
16
17
fizz
19
buzz
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment