Instantly share code, notes, and snippets.

Embed
What would you like to do?
Inventing Objects
//: Inventing Objects: A Playground exploring the creation of a simple object pattern
//
// Demonstrates:
// - Message Passing
// - Encapsulation
// - Composition/Inheritance/Delegation
// - Open Recursion (...in a crummy way...)
// - Encapsulated, mutable state
import Foundation
// Message Passing - Dynamic dispatch
typealias Message = String
typealias Object = (Message) -> Any?
let obj1: Object = { (msg: Message) in
switch msg {
case "do-something": return "sure!"
default: return nil
}
}
obj1("do-something") // passing a message is calling a function
obj1("do-other-thing")
// Encapsulation - Capturing locals with closures
let Person = { (firstName fn: String, lastName ln: String) -> Object in
return { (msg: Message) -> Any? in
switch msg {
case "full-name": return "\(fn) \(ln)"
case "description": return "<Person>"
default: return nil
}
}
}
let p = Person(firstName: "Colin", lastName: "Drake")
p("full-name")
p("description")
p("address")
let getDescription = { (o: Object) -> Any? in // function-style accessors
return o("description")
}
getDescription(p)
// Encapsulated, mutable state
let Counter = { (initial: Int) -> Object in
var counter = initial
return { (msg: Message) in
switch msg {
case "count": return counter
case "increment":
counter += 1
return counter
case "decrement":
counter -= 1
return counter
default: return nil
}
}
}
let cnt = Counter(0)
cnt("count")
cnt("increment")
cnt("increment")
cnt("decrement")
// Composition/Inheritance/Delegation - Implementing an inheritance chain for code reuse via delegation
enum RunSpeed: String {
case Slow
case Fast
}
let Athelete = { (firstName fn: String, lastName ln: String, runningSpeed rs: RunSpeed) -> Object in
let superInstance = Person(firstName: fn, lastName: ln) // compose a person
return { (msg: Message) -> Any? in
switch msg {
case "how-fast?": return rs
case "description": return "<A \(rs.rawValue)-Moving Athelete>" // override!
default: return superInstance(msg) // inheritance via delegation
}
}
}
let a = Athelete(firstName: "Colin", lastName: "Drake", runningSpeed: .Slow)
a("how-fast?")
a("description")
a("full-name")
// Open recursion - directly calling "methods" on ourself
let Cat = { (name n: String, age a: Int) -> Object in
let name = {
return "\(n) the Cat"
}
let description = {
return "<A Feline named \(name())>"
}
return { (msg: Message) -> Any? in
switch msg {
case "name": return name()
case "description": return description()
default: return nil
}
}
}
let leo = Cat(name: "Leo", age: 2)
leo("name")
leo("description")
// Open recursion - passing messages to ourself by staying in our object "system"
// h/t: https://xiliangchen.wordpress.com/2014/08/04/recursive-closure-and-y-combinator-in-swift/
// HACK: This is needed because Swift doesn't support named closures calling themselves recursively.
// This whole idea is, IMHO, expressed a bit more elegantly in a LISP-like language...
func Y<T, R>( f: (T -> R) -> (T -> R) ) -> (T -> R) {
return { t in f(Y(f))(t) }
}
let DispatchedCat = { (name n: String, age a: Int) -> Object in
return Y {
dispatch in { (msg: Message) -> Any? in
switch msg {
case "name": return "\(n) the Cat"
case "description":
let me = dispatch("name")! // dispatching on "this", not calling directly
return "<A Feline named \(me)>"
default: return nil
}
}
}
}
let bob = DispatchedCat(name: "Bob", age: 4)
bob("name")
bob("description")
// Goodie/Extra: A simple method_missing a la Ruby - responding to messages which aren't hardcoded
let SQLBuilder = { (table: String) -> Object in
return { (msg: Message) in
let field = msg.stringByReplacingOccurrencesOfString("sort-by-", withString: "")
if field != msg {
return "SELECT * FROM \(table) ORDER BY \(field)"
}
return nil
}
}
let s = SQLBuilder("user")
s("sort-by-age")
s("sort-by-name")
s("group-by-something")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment