Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Answering the Existential Question

Answering the Existential Question

Given at FrenchKit 2018

Samuel E. Giddins

September 21, 2018
Tweet

More Decks by Samuel E. Giddins

Other Decks in Technology

Transcript

  1. @segiddins Mobile Developer Experience @ Square Trill Core Team 2

    — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  2. ⚠ Programming Language Nerdery Ahead 3 — Answering the Existential

    Question – Samuel Giddins @ FrenchKit 2018
  3. back to the beginning of time 4 — Answering the

    Existential Question – Samuel Giddins @ FrenchKit 2018
  4. NSArray<NSString *> *strings = @[ @"1", @2.string, ]; 6 —

    Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  5. id strings = [NSArray arrayWithObjects: [NSString stringWithUTF8String:"1"], [[NSNumber numberWithInteger:2] stringValue],

    nil ]; 7 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  6. Objective-C ⇢Based on SmallTalk ⇢All message-passaging 8 — Answering the

    Existential Question – Samuel Giddins @ FrenchKit 2018
  7. Message Passing receiver (self) determines what to do with each

    message 9 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  8. sound familiar? we still have that in today's Objective-C --

    id 10 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  9. id is really cool every objc object is an id,

    and you can send any message to id 11 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  10. even better you can recover class/type info from an id,

    get info like respondsToSelector: 12 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  11. You can ask an id what it is, and it

    will tell you. 13 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  12. I think, therefore I am. — Descartes 14 — Answering

    the Existential Question – Samuel Giddins @ FrenchKit 2018
  13. And there you have an existential! 15 — Answering the

    Existential Question – Samuel Giddins @ FrenchKit 2018
  14. Existential: something that knows what it is 16 — Answering

    the Existential Question – Samuel Giddins @ FrenchKit 2018
  15. existentials' power: ⇢Dynamic Casting ⇢print() ⇢Protocol Types 18 — Answering

    the Existential Question – Samuel Giddins @ FrenchKit 2018
  16. When Swift was first announced... 19 — Answering the Existential

    Question – Samuel Giddins @ FrenchKit 2018
  17. When Swift was first announced I worried that Ints and

    Bools having type information would make the language slow 20 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  18. ... would make the language slow like Ruby 21 —

    Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  19. In Ruby (like Objective-C), every object has an isa pointer

    22 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  20. struct objc_object { Class isa, uint_64t flags, // ivars... }

    23 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  21. That'd be a lot of overhead for every Int! 24

    — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  22. So how does this work? let i: Int = 5

    let a: Any = i as Any if let really_i = a as? Int { print("this works! i is \(really_i)") } 25 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  23. It doesn't work in a language like C, since an

    Int is just 32 bits 26 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  24. ! Swift is smarter than C 28 — Answering the

    Existential Question – Samuel Giddins @ FrenchKit 2018
  25. Let's step through how these 5 lines of code work

    let i: Int = 5 let a: Any = i as Any if let really_i = a as? Int { print("this works! i is \(really_i)") } 29 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  26. This is equivalent to C: we just store the raw

    integer let i: Int = 5 let a: Any = i as Any if let really_i = a as? Int { print("this works! i is \(really_i)") } 30 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  27. This is just like doing an isKindOfClass: check in ObjC

    -- at runtime, we check the type metadata for a let i: Int = 5 let a: Any = i as Any if let really_i = a as? Int { print("this works! i is \(really_i)") } 31 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  28. And now really_i is back to being a normal Int

    -- and can be used with zero overhead let i: Int = 5 let a: Any = i as Any if let really_i = a as? Int { print("this works! i is \(really_i)") } 32 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  29. Oh, I missed this line? let i: Int = 5

    let a: Any = i as Any if let really_i = a as? Int { print("this works! i is \(really_i)") } 34 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  30. It turns out, this is the key to understanding how

    existentials work let a: Any = 5 as Any 35 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  31. let a: Any = 5 as Any ⇢Compiler sees a

    cast from Int Any ⇢ Int is a non-existential type, Any is existential ⇢ Compiler generates a "promotion" for 5, and stores it as a separate instance 36 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  32. let a: Any = 5 as Any ⇢A new ExistentialBox

    is allocated ⇢ Points to type metadata ⇢ Which also points to "thunks" for protocol witnesses ⇢ Points to (or copies) the underlying value 37 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  33. All code referring to an Any knows it needs to

    indirect through the Box 38 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  34. And any code that needs type metadata for a non-existential

    type Goes through the promotion process 39 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  35. Trill Wannabe Swift, written in Swift 40 — Answering the

    Existential Question – Samuel Giddins @ FrenchKit 2018
  36. type Foo { let bar: Int let baz: Bool }

    41 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  37. func foo(_ t: Any) -> Int { printf("[%s] entered\n", #function)

    if t is Int { printf("t is an Int!!\n") } return t as Int } func duplicate(_ t: Any) -> Any { return t as Int * 2 } 42 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  38. func main() { printf("[%s] entered\n", #function) let f = foo(1)

    var v: Any = 1 if !(v is Bool) { printf("v: %d\n", v as Int) } v = true if v is Bool { printf("v: %s\n", v as Bool ? "true" : "false") } v = Foo(bar: 20, baz: true) if v is Foo { let foo = v as Foo printf("v: Foo(bar: %d, baz: %s)\n", foo.bar, foo.baz ? "true" : "false") } printf("[%s] foo returned\n", #function) printf("%d\n", f) printf("%d\n", duplicate(2) as Int) } 43 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  39. /// TRILL_ANY is a special type understood by the Trill

    compiler as the /// representation of an Any value. typedef struct TRILL_ANY { void * _Nonnull _any; #ifdef __cplusplus inline AnyBox *_Nonnull any() { trill_assert(_any != nullptr && "passed a null value for Any"); return reinterpret_cast<AnyBox *>(_any); } inline AnyBox *_Nonnull operator->() noexcept { return any(); } inline operator AnyBox*_Nonnull() { return any(); } #endif } TRILL_ANY; 44 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  40. /// An AnyBox is a heap-allocated box that contains: ///

    - A pointer to the type metadata for an underlying value /// - A variably-sized payload struct AnyBox { const TypeMetadata *typeMetadata; static AnyBox *create(const TypeMetadata *metadata); AnyBox *copy(); void updateField(uint64_t fieldNum, AnyBox *newValue); void *value() { return reinterpret_cast<void *>( reinterpret_cast<uintptr_t>(this) + sizeof(AnyBox)); } void *fieldValuePtr(uint64_t fieldNum); AnyBox *extractField(uint64_t fieldNum); const FieldMetadata *fieldMetadata(uint64_t fieldNum); bool isNil(); }; 45 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  41. func codegenPromoteToAny(value: IRValue, type: DataType) -> IRValue { if case

    .any = type { if storage(for: type) == .reference { // If we're promoting an existing Any value of a reference type, just // thread it through. return value } else { // If we're promoting an existing Any value of a value type, // then this should just be a copy of the existing value. return codegenCopyAny(value: value) } } let allocateAny = codegenIntrinsic(named: "trill_allocateAny") let meta = codegenTypeMetadata(type) let castMeta = builder.buildBitCast(meta, type: PointerType.toVoid, name: "mc") let res = builder.buildCall(allocateAny, args: [castMeta], name: "aa") let valPtr = codegenAnyValuePtr(res, type: type) builder.buildStore(value, to: valPtr) return res } 46 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  42. Opaque Return Types func importantDates() -> opaque Collection where _.Element

    == Date 49 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  43. Type Erasure let ints = AnyCollection<Int> = [1, 2, 3]

    50 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018
  44. Generalized Existentials let strings: Any<Sequence where .Iterator.Element == String> =

    ["a", "b", "c"] 51 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018