Last active
February 11, 2018 15:58
-
-
Save JadenGeller/a622af216c65503ad9b9 to your computer and use it in GitHub Desktop.
Dynamic Swift
This file contains hidden or 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
| 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