Skip to content

Instantly share code, notes, and snippets.

@hsavit1
Forked from sritchie/algebra.swift
Created December 17, 2015 23:31
Show Gist options
  • Select an option

  • Save hsavit1/e4d72fcdc407eebd917e to your computer and use it in GitHub Desktop.

Select an option

Save hsavit1/e4d72fcdc407eebd917e to your computer and use it in GitHub Desktop.
Typeclasses in Swift
// Playground - noun: a place where people can play
import Cocoa
var str = "Hello, playground"
// Here's take 1. First, I defined the algebra like I would in
// Scala, as separate protocols:
protocol Semigroup {
typealias T
func plus(a: T, _ b: T) -> T
}
// And the monoid extension:
protocol Monoid: Semigroup {
func zero() -> T
}
// The problem here is you end up with these separate instances
// that implement the typeclasses. Because Swift has no implicit
// resolution, you have to go through some gymnastics to build on the
// typeclass.
class IntMonoid: Monoid {
typealias T = Int
func plus(a: Int, _ b: Int) -> Int {
return a + b
}
func zero() -> Int {
return 0
}
}
// I wrote this protocol that the types themselves could extend;
// the idea was that they could use the Monoid implementations to
// implement the functions. This is a sad but of duplication.
//
// This protocol allows you to call plus directly on an instance.
protocol Addable {
func zero() -> Self
func plus(x: Self) -> Self
}
// And an example of how to implement Addable for Int:
extension Int: Addable {
var monoid: IntMonoid { return IntMonoid() }
func zero() -> Int {
return monoid.zero()
}
func plus(x: Int) -> Int {
return monoid.plus(self, x);
}
}
// It works!!
1.plus(15)
// I can't quite get this to work (the error is "missing argument
// for parameter #1 in call"), but it doesn't matter. It's clearly
// too much duplication.
/**
// This is commented out because it screws up the playground.
func sum1<T : Addable>(array: T[]) -> T {
return array.reduce(T.zero(), combine: { $0.plus($1) })
}
**/
// For take 2, I took the approach from this post:
// http://slashmesays.tumblr.com/post/87833542239/type-classes-in-swift
protocol Semigroup2 {
class func plus(a: Self, _ b: Self) -> Self
}
// And the monoid extension:
protocol Monoid2: Semigroup2 {
class func zero() -> Self
}
// This allows you to extend Int directly:
extension Int: Monoid2 {
static func zero() -> Int {
return 0
}
static func plus(a: Int, _ b: Int) -> Int {
return a + b
}
}
// Addition is now a class method.
Int.plus(1, 2)
// And you can write global aliases for these things that LOOK
// like they're doing implicit resolution. This works because the
// generic type is aliased to the resolved, specific type within
// the body of the function:
func plus<T: Semigroup2>(a: T, b: T) -> T {
return T.plus(a, b)
}
// This works, now:
plus(1, 2)
// And you can build on top of the lower level functions:
func sum2<T : Monoid2>(array: T[]) -> T {
return array.reduce(T.zero(), combine: { plus($0, $1)})
}
// This works great for integers, and for anything else
// that implements the typeclass:
sum2([1, 2, 3])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment