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. Answering the Existential Question
    Samuel Giddins
    1

    View Slide

  2. @segiddins
    Mobile Developer Experience @ Square
    Trill Core Team
    2 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018

    View Slide


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

    View Slide

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

    View Slide

  5. Objective-C
    5 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018

    View Slide

  6. NSArray *strings = @[
    @"1",
    @2.string,
    ];
    6 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018

    View Slide

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

    View Slide

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

    View Slide

  9. Message Passing
    receiver (self) determines what to do with each
    message
    9 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018

    View Slide

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

    View Slide

  11. 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

    View Slide

  12. even better
    you can recover class/type info from an id,
    get info like respondsToSelector:
    12 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018

    View Slide

  13. You can ask an id what it is, and it will tell you.
    13 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018

    View Slide

  14. I think, therefore I am.
    — Descartes
    14 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018

    View Slide

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

    View Slide

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

    View Slide

  17. Simple Sounding,
    Powerful Construct
    17 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018

    View Slide

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

    View Slide

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

    View Slide

  20. 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

    View Slide

  21. ... would make the language slow
    like Ruby
    21 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  25. 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

    View Slide

  26. 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

    View Slide

  27. !
    27 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018

    View Slide

  28. !
    Swift is smarter than C
    28 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018

    View Slide

  29. 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

    View Slide

  30. 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

    View Slide

  31. 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

    View Slide

  32. 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

    View Slide

  33. and that's it!
    !
    33 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018

    View Slide

  34. 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

    View Slide

  35. 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

    View Slide

  36. 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

    View Slide

  37. 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

    View Slide

  38. All code referring to an Any knows it needs to indirect
    through the Box
    38 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018

    View Slide

  39. 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

    View Slide

  40. Trill
    Wannabe Swift, written in Swift
    40 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018

    View Slide

  41. type Foo {
    let bar: Int
    let baz: Bool
    }
    41 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018

    View Slide

  42. 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

    View Slide

  43. 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

    View Slide

  44. /// 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(_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

    View Slide

  45. /// 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(
    reinterpret_cast(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

    View Slide

  46. 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

    View Slide

  47. What's next?
    47 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018

    View Slide

  48. What's next for Swift?
    48 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018

    View Slide

  49. Opaque Return Types
    func importantDates() ->
    opaque Collection where _.Element == Date
    49 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018

    View Slide

  50. Type Erasure
    let ints = AnyCollection = [1, 2, 3]
    50 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018

    View Slide

  51. Generalized Existentials
    let strings: Any =
    ["a", "b", "c"]
    51 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018

    View Slide

  52. Samuel Giddins
    @segiddins
    52 — Answering the Existential Question – Samuel Giddins @ FrenchKit 2018

    View Slide