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