Last active
November 28, 2024 19:15
-
-
Save rxwei/e3ff811fdcaeac59bc14a034aebd4752 to your computer and use it in GitHub Desktop.
GADT in 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
/// A type equality guarantee is capable of safely casting one value to a | |
/// different type. It can only be created when `S` and `T` are statically known | |
/// to be equal. | |
struct TypeEqualityGuarantee<S, T> { | |
private init() {} | |
/// Safely cast a value to the other type. | |
func cast(_ value: T) -> S { | |
return value as! S | |
} | |
/// Safely cast a value to the other type. | |
func cast(_ value: S) -> T { | |
return value as! T | |
} | |
} | |
// The same-type constraint guarantees that `TypeEqualityGuarantee` is only | |
// constructed when `S` is statically known to be equal to `T`. | |
extension TypeEqualityGuarantee where S == T { | |
// Creates a type equality guarantee. | |
init() {} | |
} | |
/// Lifted arithmetic operators | |
enum Arithmetic<T : BinaryInteger> { | |
case add, subtract, multiply, divide | |
subscript(x: T, y: T) -> T { | |
switch self { | |
case .add: return x + y | |
case .subtract: return x - y | |
case .multiply: return x * y | |
case .divide: return x / y | |
} | |
} | |
} | |
/// Lifted comparison operators | |
enum Comparator<T : Comparable> { | |
case greaterThan, lessThan, equal | |
subscript(x: T, y: T) -> Bool { | |
switch self { | |
case .greaterThan: return x > y | |
case .lessThan: return x < y | |
case .equal: return x == y | |
} | |
} | |
} | |
/// Expression | |
indirect enum Expression<T> { | |
case number(TypeEqualityGuarantee<T, Int>, Int) | |
case bool(TypeEqualityGuarantee<T, Bool>, Bool) | |
case arithmetic(TypeEqualityGuarantee<T, Int>, Arithmetic<Int>, Expression<Int>, Expression<Int>) | |
case compare(TypeEqualityGuarantee<T, Bool>, Comparator<Int>, Expression<Int>, Expression<Int>) | |
case negate(TypeEqualityGuarantee<T, Bool>, Expression<Bool>) | |
} | |
extension Expression { | |
static func number(_ x: Int) -> Expression<Int> { | |
return .number(TypeEqualityGuarantee(), x) | |
} | |
static func bool(_ b: Bool) -> Expression<Bool> { | |
return .bool(TypeEqualityGuarantee(), b) | |
} | |
static func arithmetic(_ operator: Arithmetic<Int>, | |
_ lhs: Expression<Int>, | |
_ rhs: Expression<Int>) -> Expression<Int> { | |
return .arithmetic(TypeEqualityGuarantee(), `operator`, lhs, rhs) | |
} | |
static func compare(_ comparator: Comparator<Int>, | |
_ lhs: Expression<Int>, | |
_ rhs: Expression<Int>) -> Expression<Bool> { | |
return .compare(TypeEqualityGuarantee(), comparator, lhs, rhs) | |
} | |
static func negate(_ x: Expression<Bool>) -> Expression<Bool> { | |
return .negate(TypeEqualityGuarantee(), x) | |
} | |
} | |
extension Expression { | |
func evaluated() -> T { | |
switch self { | |
case let .number(proof, n): | |
return proof.cast(n) | |
case let .bool(proof, b): | |
return proof.cast(b) | |
case let .arithmetic(proof, arith, n1, n2): | |
return proof.cast(arith[n1.evaluated(), n2.evaluated()]) | |
case let .compare(proof, comp, n1, n2): | |
return proof.cast(comp[n1.evaluated(), n2.evaluated()]) | |
case let .negate(proof, n1): | |
return proof.cast(!n1.evaluated()) | |
} | |
} | |
} | |
/// Simple expression | |
let expr1 = Expression<Bool>.compare(.equal, .number(10), .number(30)) | |
print(expr1.evaluated()) /// => false | |
let expr2 = Expression<Int>.arithmetic(.add, .number(10), .number(20)) | |
print(expr2.evaluated()) /// => 30 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Static type check: