Skip to content

Instantly share code, notes, and snippets.

@TizianoCoroneo
Last active May 29, 2024 10:22
Show Gist options
  • Save TizianoCoroneo/ae263bef4d5ba4767669ba161c67c5e4 to your computer and use it in GitHub Desktop.
Save TizianoCoroneo/ae263bef4d5ba4767669ba161c67c5e4 to your computer and use it in GitHub Desktop.
Type level FizzBuzz in Swift 5.10
func namespace(_ f: () -> ()) -> () { f() }
// A fizz buzz implementation in Swift
namespace {
let range = 1...100
let result = range.map { (n: Int) -> String in
switch (n % 3, n % 5) {
case (0, 0): "Fizz Buzz"
case (0, _): "Fizz"
case (_, 0): "Buzz"
default: "\(n)"
}
}.joined(separator: "\n")
print(result)
}
// At a type level now!
// First, typelevel Peano construction of natural numbers.
protocol NumType {
associatedtype Previous: NumType
static var value: Int { get }
}
// For every natural number, its predecessor is also a natural number.
// The predecessor of 0 is `Never`, and the predecessor of `Never` is `Never`,
// tying up the `Previous` associatedtype recursion.
extension Never: NumType {
typealias Previous = Never
static var value: Int { fatalError() }
}
// Define 0 and the successor of a natural number.
// This way I get all the numbers by induction.
enum NZero: NumType {
typealias Previous = Never
static var value: Int { return 0 } }
enum NSucc<N: NumType>: NumType {
typealias Previous = N
static var value: Int { return N.value + 1 } }
typealias N1 = NSucc<NZero>
typealias N2 = NSucc<N1>
typealias N3 = NSucc<N2>
typealias N4 = NSucc<N3>
typealias N5 = NSucc<N4>
typealias N6 = NSucc<N5>
typealias N7 = NSucc<N6>
typealias N8 = NSucc<N7>
typealias N9 = NSucc<N8>
typealias N10 = NSucc<N9>
typealias N11 = NSucc<N10>
typealias N12 = NSucc<N11>
typealias N13 = NSucc<N12>
typealias N14 = NSucc<N13>
typealias N15 = NSucc<N14>
// Now Fizz Buzz!
protocol Fizz {}
protocol Buzz {}
typealias FizzBuzz = Fizz & Buzz
// This is an unfortunate side effect of this implementation:
// I have to make the 0 both fizz and buzz to tie the associatedtype recursion up.
extension NZero: Fizz {}
extension NZero: Buzz {}
// Then I can define:
// a number is Fizz if the number - 3 is also Fizz
// a number is Buzz if the number - 5 is also Buzz
extension NSucc: Fizz where Previous.Previous.Previous: Fizz {}
extension NSucc: Buzz where Previous.Previous.Previous.Previous.Previous: Buzz {}
// Some utilities to check if a type number is Fizz, Buzz or FizzBuzz
// Should be probably easier to check in Swift 5.1, using the new `some` keyword for opaque result types.
func testFizz<N: NumType>(_ n: N.Type) where N: Fizz {}
func testBuzz<N: NumType>(_ n: N.Type) where N: Buzz {}
func testFizzBuzz<N: NumType>(_ n: N.Type) where N: FizzBuzz {}
// The commented lines result in compile-time errors.
testFizz(NZero.self)
//testFizz(N1.self)
//testFizz(N2.self)
testFizz(N3.self)
//testFizz(N4.self)
testBuzz(N5.self)
testFizz(N6.self)
//testFizz(N7.self)
//testFizz(N8.self)
testFizz(N9.self)
testBuzz(N10.self)
//testBuzz(N11.self)
testFizz(N12.self)
//testFizz(N13.self)
//testFizz(N14.self)
testFizzBuzz(N15.self)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment