Last active
August 29, 2015 13:58
-
-
Save ceedubs/10122016 to your computer and use it in GitHub Desktop.
Type level 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
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