Created
June 9, 2014 02:57
-
-
Save sritchie/27bdf9329027d5cdf02e to your computer and use it in GitHub Desktop.
Typeclasses in Swift
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
// 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
I was toying with typeclasses in swift and then found your gist. Great to see others have been playing around with this too!
This is what I got: