Skip to content

Instantly share code, notes, and snippets.

@JadenGeller
Last active February 11, 2018 15:58
Show Gist options
  • Select an option

  • Save JadenGeller/a622af216c65503ad9b9 to your computer and use it in GitHub Desktop.

Select an option

Save JadenGeller/a622af216c65503ad9b9 to your computer and use it in GitHub Desktop.
Dynamic Swift
import Foundation
// Dynamic swift!
/* * * * * * * * * * * * * * * * * * * * *
* Scroll to the bottom for sample code! *
* * * * * * * * * * * * * * * * * * * * */
// Disclaimer: Any resemblance to JavaScript or any other shitty language is completely accidental.
typealias Object = [JValue : JValue]
typealias Function = [JValue] -> JValue
enum JValue : BooleanLiteralConvertible, BooleanType, StringLiteralConvertible, Printable, Equatable, Hashable, FloatLiteralConvertible, IntegerLiteralConvertible, DictionaryLiteralConvertible, NilLiteralConvertible, ArrayLiteralConvertible {
case JUndefined
case JNull
case JNumber(Double)
case JString(String)
case JBoolean(Bool)
case JObject(Object)
case JFunction(Function)
init(booleanLiteral boolValue: Bool) { self = .JBoolean(boolValue) }
init(extendedGraphemeClusterLiteral value: String) { self = .JString(value) }
init(unicodeScalarLiteral value: String) { self = .JString(value) }
init(stringLiteral value: String) { self = .JString(value) }
init(integerLiteral value: Int) { self = .JNumber(Double(value)) }
init(floatLiteral value: Double) { self = .JNumber(value) }
init(dictionaryLiteral elements: (String, JValue)...) {
var obj = Object()
for (key, value) in elements { obj[.JString(key)] = value }
self = .JObject(obj)
}
init(nilLiteral: ()) { self = .JNull }
init(arrayLiteral elements: JValue...) {
var obj = Object()
for (i,x) in enumerate(elements) { obj[.JNumber(Double(i))] = x }
self = .JObject(obj)
}
var numberValue: JValue {
get {
switch self {
case .JUndefined: return .JNumber(Double.NaN)
case .JNull: return .JNumber(0)
case .JNumber: return self
case .JString(let str):
let num = NSNumberFormatter().numberFromString(str)
return .JNumber(num != nil ? num!.doubleValue : Double.NaN)
case .JBoolean(let b): return .JNumber(b ? 1.0 : 0.0)
case .JObject: return .JNumber(Double.NaN)
case .JFunction: return .JNumber(Double.NaN)
}
}
}
var primitiveValue: JValue {
get {
switch self {
case .JObject(let obj):
if let valueFunction = obj["valueOf"] {
return callFunction(valueFunction,self)
}
else {
return .JString(self.description)
}
default: return self
}
}
}
var boolValue: Bool {
get {
switch self {
case .JUndefined: return false
case .JNull: return false
case .JNumber(let n): return n != 0 && n != Double.NaN
case .JString(let s): return count(s) > 0
case .JBoolean(let b): return b
case .JObject: return true
case .JFunction: return false
}
}
}
func callFunction(args: JValue...) -> JValue {
switch self {
case .JFunction(let f): return f(args)
default: fatalError("Non-function called as a function.")
}
}
var description: String {
get {
switch self {
case .JUndefined: return "undefined"
case .JNull: return "null"
case .JNumber(let n): return n.description
case .JString(let s): return s
case .JBoolean(let b): return b.description
case .JObject(let o): return o.description
case .JFunction(let f): return "\(f)"
}
}
}
var hashValue: Int {
get {
switch self {
case .JUndefined: return 0
case .JNull: return 1
case .JNumber(let n): return n.hashValue
case .JString(let s): return s.hashValue
case .JBoolean(let b): return b.hashValue
case .JObject(let o): return reduce(Array(o.values), 0, { r, a in r ^ a.hashValue })
case .JFunction(let f): return -1
}
}
}
}
func ===(lhs: JValue, rhs: JValue) -> JValue {
let b: Bool = lhs == rhs
return .JBoolean(b)
}
func ==(lhs: JValue, rhs: JValue) -> Bool {
switch (lhs, rhs) {
case (.JUndefined , .JUndefined) : return true
case (.JNull , .JNull) : return true
case (.JNumber (let l), .JNumber(let r)) : return l == r
case (.JString (let l), .JString(let r)) : return l == r
case (.JBoolean (let l), .JBoolean(let r)) : return l == r
case (.JObject (let l), .JObject(let r)) : return l == r
case (.JFunction(let l), .JFunction(let r)) : return false
default : return false
}
}
func ==(lhs: JValue, rhs: JValue) -> JValue {
switch lhs {
case .JUndefined:
switch rhs {
case .JUndefined: return true
case .JNull: return true
case .JNumber: return false
case .JString: return false
case .JBoolean: return false
case .JObject: return false
case .JFunction: return false
}
case .JNull:
switch rhs {
case .JUndefined: return true
case .JNull: return true
case .JNumber: return false
case .JString: return false
case .JBoolean: return false
case .JObject: return false
case .JFunction: return false
}
case .JNumber:
switch rhs {
case .JUndefined: return false
case .JNull: return false
case .JNumber: return lhs === rhs
case .JString: return lhs === rhs.numberValue
case .JBoolean: return lhs === rhs.numberValue
case .JObject: return lhs == rhs.primitiveValue
case .JFunction: return false
}
case .JString:
switch rhs {
case .JUndefined: return false
case .JNull: return false
case .JNumber: return lhs.numberValue === rhs
case .JString: return lhs === rhs
case .JBoolean: return lhs.numberValue === rhs.numberValue
case .JObject: return lhs == rhs.primitiveValue
case .JFunction: return false
}
case .JBoolean:
switch rhs {
case .JUndefined: return false
case .JNull: return false
case .JNumber: return lhs.numberValue === rhs
case .JString: return lhs.numberValue === rhs.numberValue
case .JBoolean: return lhs === rhs
case .JObject: return false
case .JFunction: return false
}
case .JObject:
switch rhs {
case .JUndefined: return false
case .JNull: return false
case .JNumber: return lhs.primitiveValue == rhs
case .JString: return lhs.primitiveValue == rhs
case .JBoolean: return false
case .JObject: return lhs === rhs
case .JFunction: return false
}
case .JFunction:
switch rhs {
case .JUndefined: return false
case .JNull: return false
case .JNumber: return false
case .JString: return false
case .JBoolean: return false
case .JObject: return false
case .JFunction: return lhs === rhs
}
}
}
// Let's check out a few examples of how a single variable can represent a bunch of different types!
var x: JValue
x = 10
x = "Hello"
// Our dictionaries can be keyed by any types of values, and we can mix-and-match the value types. Super cool!
x = ["name" : "Jaden", "age" : 19, "alive" : true, "schools" : ["Folsom High", "Caltech"]]
// Functions can accept any type too!
func test(x: JValue) {
if x === 10 {
println("Whoa way better than a high five!")
}
else if x === "Hi" {
println("Well hello there... *blushes*")
}
else {
println("Who even are you!")
}
}
test(10) // -> Whoa way better than a high five!
test("Hi") // -> Well hello there... *blushes*
test(nil) // -> Who even are you!
// We can test if two values are the same even if they aren't the same type :)
let result: JValue = 10 == "10"
println(result) // -> true
// We're able to test strict equality too.
let otherResult: JValue = 10 === "10"
println(otherResult) // -> false
// Pretty cool, huh!
// Obviously this is missing A LOT of features, but it's just time+effort to implement them, nothing tricky.
// The main takeaway here is that statically typed langagues are a subset of dynamically typed langagues.
// We can emulate a dynamically typed language within a statically typed language because a dynamically typed
// language is just a staticly typed lanague with a single type---a unityped language. Sum types to the rescue!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment