Created
March 22, 2016 00:21
-
-
Save cfdrake/2a20fe432547d92b50a8 to your computer and use it in GitHub Desktop.
Inventing Objects
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
//: 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