Skip to content

Instantly share code, notes, and snippets.

@xwu
Last active August 3, 2017 02:07
Show Gist options
  • Select an option

  • Save xwu/d68baefaae9e9291d2e65bd12ad51be2 to your computer and use it in GitHub Desktop.

Select an option

Save xwu/d68baefaae9e9291d2e65bd12ad51be2 to your computer and use it in GitHub Desktop.

Revisions

  1. xwu revised this gist Jun 18, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion user-experience-of-new-integer-protocols.md
    Original file line number Diff line number Diff line change
    @@ -119,7 +119,7 @@ For a type such as `Rational<T>`, where the numerator and denominator are of typ

    ### Suggestion for improvement

    From a user perspective, the semantics of `ExpressibleByIntegerLiteral` suggest that a generic type that has exclusively stored properties of type `T where T : ExpressibleByIntegerLiteral` should be conformable to `ExpressibleByIntegerLiteral` by implementing an initializer that accepts a literal value of type `T`--without being constrained to any underscored protocol. I suspect, however, that this is not a trivial detail to implement in the compiler.
    From a user perspective, the semantics of `ExpressibleByIntegerLiteral` suggest that a generic type that has exclusively stored properties of type `T where T : ExpressibleByIntegerLiteral` should be conformable to `ExpressibleByIntegerLiteral` by implementing an initializer that accepts a literal value of type `T`--without being constrained to any underscored protocol. I suspect, however, that this is not a trivial detail to implement, especially without recursive protocol constraints.


    ## Implementations of heterogeneous comparison and bit shift operators
  2. xwu revised this gist Jun 18, 2017. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion user-experience-of-new-integer-protocols.md
    Original file line number Diff line number Diff line change
    @@ -95,7 +95,8 @@ A minor re-design of related protocols appears to be necessary for implementatio
    * In the protocol `ExpressibleByIntegerLiteral`, `associatedtype IntegerLiteralType` should be constrained to `_ExpressibleByBuiltinIntegerLiteral & BinaryInteger` (instead of `_ExpressibleByBuiltinIntegerLiteral` alone). This may give rise to a recursive constraint and require a workaround to implement at the present moment. Namely:

    ```swift
    public protocol _ExpressibleByIntegerLiteral : ExpressibleByIntegerLiteral where IntegerLiteralType : BinaryInteger { }
    public protocol _ExpressibleByIntegerLiteral : ExpressibleByIntegerLiteral
    where IntegerLiteralType : BinaryInteger { }
    /* ... */
    public protocol BinaryInteger : /* ... */, _ExpressibleByIntegerLiteral { }
    ```
  3. xwu revised this gist Jun 18, 2017. 1 changed file with 7 additions and 1 deletion.
    8 changes: 7 additions & 1 deletion user-experience-of-new-integer-protocols.md
    Original file line number Diff line number Diff line change
    @@ -92,7 +92,13 @@ func k<T : FixedWidthInteger>(_: T.Type) -> Bool {

    A minor re-design of related protocols appears to be necessary for implementation of this rule give the appropriate behavior. Namely:

    * In the protocol `ExpressibleByIntegerLiteral`, `associatedtype IntegerLiteralType` should be constrained to `_ExpressibleByBuiltinIntegerLiteral & BinaryInteger` (instead of `_ExpressibleByBuiltinIntegerLiteral` alone). This may give rise to a recursive constraint and require a workaround to implement at the present moment.
    * In the protocol `ExpressibleByIntegerLiteral`, `associatedtype IntegerLiteralType` should be constrained to `_ExpressibleByBuiltinIntegerLiteral & BinaryInteger` (instead of `_ExpressibleByBuiltinIntegerLiteral` alone). This may give rise to a recursive constraint and require a workaround to implement at the present moment. Namely:

    ```swift
    public protocol _ExpressibleByIntegerLiteral : ExpressibleByIntegerLiteral where IntegerLiteralType : BinaryInteger { }
    /* ... */
    public protocol BinaryInteger : /* ... */, _ExpressibleByIntegerLiteral { }
    ```

    * In the protocol `BinaryInteger`, a default implementation is required as follows:

  4. xwu revised this gist Jun 18, 2017. 1 changed file with 12 additions and 1 deletion.
    13 changes: 12 additions & 1 deletion user-experience-of-new-integer-protocols.md
    Original file line number Diff line number Diff line change
    @@ -81,7 +81,7 @@ j(42) // Prints "Int 42".
    j(.init(integerLiteral: 42)) // Prints "Int 42".
    ```

    Intriguingly, `T.init(integerLiteral: 0)` is currently ambiguous where `T : FixedWidthInteger`, suggesting that some re-design of the protocol would also be necessary to have this rule give the appropriate behavior.
    Intriguingly, `T.init(integerLiteral: 0)` is currently ambiguous where `T : FixedWidthInteger`:

    ```swift
    func k<T : FixedWidthInteger>(_: T.Type) -> Bool {
    @@ -90,6 +90,17 @@ func k<T : FixedWidthInteger>(_: T.Type) -> Bool {
    }
    ```

    A minor re-design of related protocols appears to be necessary for implementation of this rule give the appropriate behavior. Namely:

    * In the protocol `ExpressibleByIntegerLiteral`, `associatedtype IntegerLiteralType` should be constrained to `_ExpressibleByBuiltinIntegerLiteral & BinaryInteger` (instead of `_ExpressibleByBuiltinIntegerLiteral` alone). This may give rise to a recursive constraint and require a workaround to implement at the present moment.

    * In the protocol `BinaryInteger`, a default implementation is required as follows:

    ```swift
    public init(integerLiteral value: IntegerLiteralType) {
    self.init(value)
    }
    ```

    ## `_ExpressibleByBuiltinIntegerLiteral`

  5. xwu revised this gist Jun 18, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion user-experience-of-new-integer-protocols.md
    Original file line number Diff line number Diff line change
    @@ -133,7 +133,7 @@ However, _all_ binary integers must implement `&<<` and `&>>`. For an arbitrary-

    The semantics of smart shift should be clarified to state that the right-hand operand is preprocessed by masking its bits to produce a value in the range `0..<x` where `x` is the **maximum** number of bits in a value of the same type as the left-hand operand. This is equivalent to viewing arbitrary-width integers _for the purposes of bitwise operations_ as notionally infinite sequences of bits sign-extended from the two's-complement representation of the integral value.

    For the purposes of implementation, `BinaryInteger` may require a new static property `maxBitWidth`, which would be equal to `bitWidth` for fixed-width integers and could be defined as `Int.max` for arbitrary-precision integers.
    For the purposes of implementation, `BinaryInteger` may require a new static property `maxBitWidth`, which would be equal to `bitWidth` for fixed-width integers and could be defined as `Int.max` for arbitrary-precision integers. Alternatively, add a new static property `isFixedWidth` to serve a purpose analogous to `isSigned`. The implementation of `<<` and `>>` would then make use of the added properties to give the expected behavior.


    ## `ArithmeticOverflow`
  6. xwu revised this gist Jun 17, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion user-experience-of-new-integer-protocols.md
    Original file line number Diff line number Diff line change
    @@ -158,7 +158,7 @@ The new `BinaryInteger` protocol has the following requirements:
    * `init?<T : FloatingPoint>(exactly source: T)`
    * `init<T : FloatingPoint>(_ source: T)`

    However, in my experience, it does not appear possible (or at least, it is not apparent even after some careful thought) how to implement these requirement solely with the properties and methods required by `FloatingPoint`. There do, however, exist straightforward ways to convert `BinaryFloatingPoint` values to `BinaryInteger` values.
    However, in my experience, it does not appear possible (or at least, it is not apparent even after some careful thought) how to implement these requirements solely with the properties and methods required by `FloatingPoint`. There do, however, exist straightforward ways to convert `BinaryFloatingPoint` values to `BinaryInteger` values.

    In the standard library, concrete integer types implement conversions to and from concrete built-in floating-point types. However, whether coincidentally or not, the standard library itself has not yet implemented these protocol requirements. At last check, the entire implementation of the first requirement was:

  7. xwu revised this gist Jun 17, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion user-experience-of-new-integer-protocols.md
    Original file line number Diff line number Diff line change
    @@ -169,6 +169,6 @@ In the standard library, concrete integer types implement conversions to and fro
    }
    ```

    ### Suggestions for improvement
    ### Suggestion for improvement

    Consider changing the protocol requirement so that `T` is constrained as follows: `T : BinaryFloatingPoint`.
  8. xwu revised this gist Jun 17, 2017. 1 changed file with 14 additions and 12 deletions.
    26 changes: 14 additions & 12 deletions user-experience-of-new-integer-protocols.md
    Original file line number Diff line number Diff line change
    @@ -6,7 +6,7 @@ June 17, 2017

    ## Introduction

    The design, re-design, and implementation of [SE-0104][1], a proposal to revise integer protocols in Swift, is now largely complete in shipping previews of Swift 4.0. As an exercise, I have used the new APIs to develop a [set of additional numeric facilities][2]. Here are some insights gained from that experience and suggestions for improvement based on that experience.
    The design, re-design, and implementation of [SE-0104][1], a proposal to revise integer protocols in Swift, is now largely complete in shipping previews of Swift 4. As an exercise, I have used the new APIs to develop a [set of additional numeric facilities][2]. Here are some insights gained from that experience--and from two unpublished exercises to implement a BigInt type (one from scratch, one wrapping GMP)--as well as suggestions for improvement.

    [1]: https://github.com/apple/swift-evolution/blob/master/proposals/0104-improved-integers.md
    [2]: https://github.com/xwu/NumericAnnex
    @@ -56,17 +56,17 @@ The reason that `h(_:)` gives a surprising result is explained as follows:

    * Each concrete integer type implements its own overload of the homogeneous comparison operator, whereas protocol extension methods implement heterogeneous comparison operators. When an integer literal is used on the right-hand side of the comparison, the compiler looks first to the concrete type for an suitable implementation of the operator and finds the homogeneous overload. Therefore, it does _not_ traverse the protocol hierarchy and instead infers the literal to be of the same type as the left-hand side.

    * In generic code, _even if_ the most refined protocol implements its own overload of the homogeneous comparison operator, the compiler will look for all overloads of the operator by traversing the entire protocol hierarchy. Since heterogeneous comparison operators are defined somewhere along the hierarchy, the will _always_ find an overload that accepts the "preferred" integer literal type (`Swift.IntegerLiteralType`, aka `Int`) and infers the literal to be of type `Int`.
    * In generic code, _even if_ the most refined protocol implements its own overload of the homogeneous comparison operator, the compiler will look for all overloads of the operator by traversing the entire protocol hierarchy. Since heterogeneous comparison operators are defined somewhere along the hierarchy, the compiler will _always_ find an overload that accepts the "preferred" integer literal type (`Swift.IntegerLiteralType`, aka `Int`) and infers the literal to be of type `Int`.

    Therefore, in the invocation `h(UInt.self)`, we are actually comparing `UInt.max` to `~(0 as Int)`. This is a surprising result, as evidenced by the fact that a bug nearly slipped into the standard library itself.

    ### Suggestion for improvement

    Based on the demonstration that `T.max == .max` infers the correct type in both concrete and generic code, I would suggest that type inference for integer literals be changed based on the following (notional) rule:
    Based on the demonstration that the expression `T.max == .max` infers the correct type in both concrete and generic code, I would suggest that type inference for integer literals be changed based on the following (notional) rule:

    * **Any use of an integer literal `x` should be equivalent to the use of an expression `.init(integerLiteral: x)`.**
    * **Any use of an integer literal `x` should be equivalent to the use of an implicit member expression `.init(integerLiteral: x)`.**

    This preserves the current behavior that an integer literal will preferentially be inferred to be of type `IntegerLiteralType`:
    This generally preserves the current behavior that an integer literal will preferentially be inferred to be of type `IntegerLiteralType`:

    ```swift
    func j(_ x: Int) {
    @@ -101,14 +101,16 @@ For a type such as `Rational<T>`, where the numerator and denominator are of typ

    ### Suggestion for improvement

    From a user perspective, the semantics of `ExpressibleByIntegerLiteral` suggest that a generic type that has exclusively stored properties of type `T where T : ExpressibleByIntegerLiteral` should be conformable to `ExpressibleByIntegerLiteral` by implementing an initializer that accepts a literal value of type `T`--without being constrained to any underscored protocol. I suspect that this is not a trivial detail to implement in the compiler.
    From a user perspective, the semantics of `ExpressibleByIntegerLiteral` suggest that a generic type that has exclusively stored properties of type `T where T : ExpressibleByIntegerLiteral` should be conformable to `ExpressibleByIntegerLiteral` by implementing an initializer that accepts a literal value of type `T`--without being constrained to any underscored protocol. I suspect, however, that this is not a trivial detail to implement in the compiler.


    ## Implementations of heterogeneous comparison and bit shift operators

    In Swift, there is a distinction between **default implementations** of protocol requirements and **protocol extension methods** which are not requirements. Both are defined in extensions to protocols, but default implementations are dynamically dispatched and can be overridden by conforming types, while extension methods can be shadowed but never overridden. (For example, homogeneous `==` is a protocol requirement of `Equatable`, but homogeneous `!=` is a protocol extension method that cannot be overridden.)

    The heterogeneous comparison and bit shift operators introduced in Swift 4.0 by SE-0104 are **protocol extension methods**.
    > The paragraph above is meant to provide some background on existing features of the language, but it is meant neither to question their existence in the language nor to question the design of `Equatable` and `Comparable`, which are far outside the scope of improving the revised integer protocols.
    The heterogeneous comparison and bit shift operators introduced in Swift 4 by SE-0104 are **protocol extension methods**.

    Consider a custom `BigInt` type and the comparison `(42 as BigInt) == (21 as UInt)`. There is an efficient way to perform that comparison which does not involve first converting the `UInt` value to a `BigInt` value. However, even if `BigInt` manually implements this specialized comparison operator, a generic algorithm implemented for all binary integers (say, for integer exponentiation) will use the less efficient standard library implementation of heterogeneous `==`. By contrast, the same algorithm will use the most specialized implementation of homogeneous `==`, because that function is a protocol requirement that is dynamically dispatched.

    @@ -125,7 +127,7 @@ Smart shifts, spelled `<<` and `>>`, are protocol extension methods that always

    However, because there is sometimes a performance cost for branches that handle overshifting and undershifting that cannot be optimized away, Swift now offers masking shifts, spelled `&<<` and `&>>`. As explained in SE-0104, "[a] masking shift logically preprocesses the right[-]hand operand by masking its bits to produce a value in the range `0...(x-1)` where `x` is the number of bits in the left[-]hand operand." These semantics for masking shifts make sense when the number of bits in the left-hand operand is fixed, as is the case for all fixed-width integers.

    However, all binary integers must implement `&<<` and `&>>`. For an arbitrary-width integer type (which, for various reasons, are best represented as sign-and-magnitude rather than two's-complement), the literal interpretation of those semantics is problematic. For example, most users might think that `(1 as BigInt) << 1000` should not result in overshift. However, the number of bits in the left-hand operand is 2, and therefore a plausible interpretation of the semantics of `&<<` would have `(1 as BigInt) &<< 1000 == 1`. By extension, therefore, `(1 as BigInt) << 1000 == 0`; this is a counterintuitive result.
    However, _all_ binary integers must implement `&<<` and `&>>`. For an arbitrary-width integer type (which, for various reasons, are best represented as sign-and-magnitude rather than two's-complement), the literal interpretation of those semantics is problematic. For example, most users might think that `(1 as BigInt) << 1000` should not result in overshift. However, the number of bits in the left-hand operand is 2, and therefore a plausible interpretation of the semantics of `&<<` would have `(1 as BigInt) &<< 1000 == 1`. By extension, therefore, `(1 as BigInt) << 1000 == 0`; this is a counterintuitive result.

    ### Suggestion for improvement

    @@ -136,17 +138,17 @@ For the purposes of implementation, `BinaryInteger` may require a new static pro

    ## `ArithmeticOverflow`

    With SE-0104, the `addWithOverflow` family of arithmetic operations are not longer static. They have been renamed `addingReportingOverflow` and the like, and their return type has been changed from `(T, Bool)` to `(partialValue: T, overflow: ArithmeticOverflow)`. `ArithmeticOverflow` is an enum with two cases, `overflow` and `none`. Initially, this may appear to be an improvement in terms of readability.
    With SE-0104, the `addWithOverflow` family of arithmetic operations are not longer `static`. They have been renamed `addingReportingOverflow` and the like, and their return type has been changed from `(T, Bool)` to `(partialValue: T, overflow: ArithmeticOverflow)`. `ArithmeticOverflow` is an enum with two cases, `overflow` and `none`. Initially, this may appear to be an improvement in terms of readability.

    However, in actual use, `ArithmeticOverflow` is extremely lacking in ergonomics. Where previously it was possible to destructure the tuple and evaluate overflow by writing `if overflow { ... }`, it is now required to use a much more verbose incantation: `if overflow == .overflow { ... }`. There is little else one can do with an `ArithmeticOverflow` result besides converting it into a value of type `Bool`. When working with these operations repeatedly--and, chances are that if you need such an operation, you don't need it just once--this quickly becomes very cumbersome.

    Inside the standard library, the `ArithmeticOverflow` values returned from a `*ReportingOverflow` function is created by calling an initializer that takes a `Bool` argument. Therefore, with every call to a `*ReportingOverflow` function, a `Bool` is converted to an `ArithmeticOverflow`, returned, then converted by the user back into a `Bool`. Worse, based on comments in the standard library, it appears that the compiler cannot currently elide these operations.
    Inside the standard library, the `ArithmeticOverflow` values returned from `*ReportingOverflow` functions are created by calling an initializer that takes a `Bool` argument. Therefore, with every call to a `*ReportingOverflow` function, a `Bool` is converted to an `ArithmeticOverflow`, returned, then converted by the user back into a `Bool`. Worse, based on comments in the standard library, it appears that the compiler cannot currently elide these operations.

    ### Suggestions for improvement

    1. Change the return type of `*ReportingOverflow` functions to `(partialValue: T, overflow: Bool)` (or, `didOverflow`) and eliminate the `ArithmeticOverflow` type. In practice, this would require deprecating the current functions and providing new overloads.

    2. Since the presence of type inference may cause (1) to be source-breaking, even with proper deprecations, avoid creating a source-breaking change by naming the revised functions something else. Specifically, take the opportunity to improve the name of these functions, changing `addingReportingOverflow` to `addingWithOverflowReporting` (_mutatis mutandis_ for the remaining functions). This avoids the "-ing -ing" clash and eliminates the nonsensical interpretation of the phrase "adding, reporting overflow by 42."
    2. Since the presence of type inference may cause (1) to be source-breaking even with proper deprecations, avoid creating a source-breaking change by naming the revised functions something else. Specifically, take the opportunity to improve the name of these functions, changing `addingReportingOverflow` to `addingWithOverflowReporting` (_mutatis mutandis_ for the remaining functions). This avoids the "-ing -ing" clash and eliminates the nonsensical interpretation of the phrase "adding, reporting overflow by 42."


    ## Integers from a floating-point source
    @@ -158,7 +160,7 @@ The new `BinaryInteger` protocol has the following requirements:

    However, in my experience, it does not appear possible (or at least, it is not apparent even after some careful thought) how to implement these requirement solely with the properties and methods required by `FloatingPoint`. There do, however, exist straightforward ways to convert `BinaryFloatingPoint` values to `BinaryInteger` values.

    In the standard library, concrete integer types implement conversions to and from concrete built-in floating-point types. However, whether coincidentally or no, the standard library itself has not yet implemented the protocol requirements themselves. At last check, the entire implementation of the first requirement was:
    In the standard library, concrete integer types implement conversions to and from concrete built-in floating-point types. However, whether coincidentally or not, the standard library itself has not yet implemented these protocol requirements. At last check, the entire implementation of the first requirement was:

    ```swift
    public init?<T : FloatingPoint>(exactly source: T) {
  9. xwu revised this gist Jun 17, 2017. 1 changed file with 35 additions and 1 deletion.
    36 changes: 35 additions & 1 deletion user-experience-of-new-integer-protocols.md
    Original file line number Diff line number Diff line change
    @@ -18,7 +18,7 @@ The design, re-design, and implementation of [SE-0104][1], a proposal to revise
    3. Overriding implementations of heterogeneous comparison and bit shift operators
    4. Defining the semantics of masking shifts for arbitrary-precision integers
    5. Using `ArithmeticOverflow`
    6. Initializing from a floating-point source
    6. Initializing integers from a floating-point source


    ## Heterogeneous comparison with integer literals
    @@ -136,3 +136,37 @@ For the purposes of implementation, `BinaryInteger` may require a new static pro

    ## `ArithmeticOverflow`

    With SE-0104, the `addWithOverflow` family of arithmetic operations are not longer static. They have been renamed `addingReportingOverflow` and the like, and their return type has been changed from `(T, Bool)` to `(partialValue: T, overflow: ArithmeticOverflow)`. `ArithmeticOverflow` is an enum with two cases, `overflow` and `none`. Initially, this may appear to be an improvement in terms of readability.

    However, in actual use, `ArithmeticOverflow` is extremely lacking in ergonomics. Where previously it was possible to destructure the tuple and evaluate overflow by writing `if overflow { ... }`, it is now required to use a much more verbose incantation: `if overflow == .overflow { ... }`. There is little else one can do with an `ArithmeticOverflow` result besides converting it into a value of type `Bool`. When working with these operations repeatedly--and, chances are that if you need such an operation, you don't need it just once--this quickly becomes very cumbersome.

    Inside the standard library, the `ArithmeticOverflow` values returned from a `*ReportingOverflow` function is created by calling an initializer that takes a `Bool` argument. Therefore, with every call to a `*ReportingOverflow` function, a `Bool` is converted to an `ArithmeticOverflow`, returned, then converted by the user back into a `Bool`. Worse, based on comments in the standard library, it appears that the compiler cannot currently elide these operations.

    ### Suggestions for improvement

    1. Change the return type of `*ReportingOverflow` functions to `(partialValue: T, overflow: Bool)` (or, `didOverflow`) and eliminate the `ArithmeticOverflow` type. In practice, this would require deprecating the current functions and providing new overloads.

    2. Since the presence of type inference may cause (1) to be source-breaking, even with proper deprecations, avoid creating a source-breaking change by naming the revised functions something else. Specifically, take the opportunity to improve the name of these functions, changing `addingReportingOverflow` to `addingWithOverflowReporting` (_mutatis mutandis_ for the remaining functions). This avoids the "-ing -ing" clash and eliminates the nonsensical interpretation of the phrase "adding, reporting overflow by 42."


    ## Integers from a floating-point source

    The new `BinaryInteger` protocol has the following requirements:

    * `init?<T : FloatingPoint>(exactly source: T)`
    * `init<T : FloatingPoint>(_ source: T)`

    However, in my experience, it does not appear possible (or at least, it is not apparent even after some careful thought) how to implement these requirement solely with the properties and methods required by `FloatingPoint`. There do, however, exist straightforward ways to convert `BinaryFloatingPoint` values to `BinaryInteger` values.

    In the standard library, concrete integer types implement conversions to and from concrete built-in floating-point types. However, whether coincidentally or no, the standard library itself has not yet implemented the protocol requirements themselves. At last check, the entire implementation of the first requirement was:

    ```swift
    public init?<T : FloatingPoint>(exactly source: T) {
    // FIXME(integers): implement
    fatalError()
    }
    ```

    ### Suggestions for improvement

    Consider changing the protocol requirement so that `T` is constrained as follows: `T : BinaryFloatingPoint`.
  10. xwu revised this gist Jun 17, 2017. 1 changed file with 80 additions and 2 deletions.
    82 changes: 80 additions & 2 deletions user-experience-of-new-integer-protocols.md
    Original file line number Diff line number Diff line change
    @@ -3,6 +3,7 @@
    Xiaodi Wu
    June 17, 2017


    ## Introduction

    The design, re-design, and implementation of [SE-0104][1], a proposal to revise integer protocols in Swift, is now largely complete in shipping previews of Swift 4.0. As an exercise, I have used the new APIs to develop a [set of additional numeric facilities][2]. Here are some insights gained from that experience and suggestions for improvement based on that experience.
    @@ -14,11 +15,12 @@ The design, re-design, and implementation of [SE-0104][1], a proposal to revise

    1. Performing heterogeneous comparison with integer literals
    2. Conforming to `_ExpressibleByBuiltinIntegerLiteral`
    3. Overriding heterogeneous comparison and bit shift operators
    4. Masking shifts and arbitrary-width integers
    3. Overriding implementations of heterogeneous comparison and bit shift operators
    4. Defining the semantics of masking shifts for arbitrary-precision integers
    5. Using `ArithmeticOverflow`
    6. Initializing from a floating-point source


    ## Heterogeneous comparison with integer literals

    SE-0104 added heterogeneous comparison and bit shift operators to the language to improve the user experience (for example, you can now check if an `Int` value is equal to a `UInt` value).<sup>[1](#fn1)</sup>
    @@ -58,3 +60,79 @@ The reason that `h(_:)` gives a surprising result is explained as follows:

    Therefore, in the invocation `h(UInt.self)`, we are actually comparing `UInt.max` to `~(0 as Int)`. This is a surprising result, as evidenced by the fact that a bug nearly slipped into the standard library itself.

    ### Suggestion for improvement

    Based on the demonstration that `T.max == .max` infers the correct type in both concrete and generic code, I would suggest that type inference for integer literals be changed based on the following (notional) rule:

    * **Any use of an integer literal `x` should be equivalent to the use of an expression `.init(integerLiteral: x)`.**

    This preserves the current behavior that an integer literal will preferentially be inferred to be of type `IntegerLiteralType`:

    ```swift
    func j(_ x: Int) {
    print("Int", x)
    }

    func j(_ x: UInt) {
    print("UInt", x)
    }

    j(42) // Prints "Int 42".
    j(.init(integerLiteral: 42)) // Prints "Int 42".
    ```

    Intriguingly, `T.init(integerLiteral: 0)` is currently ambiguous where `T : FixedWidthInteger`, suggesting that some re-design of the protocol would also be necessary to have this rule give the appropriate behavior.

    ```swift
    func k<T : FixedWidthInteger>(_: T.Type) -> Bool {
    return (0 as T) == T.init(integerLiteral: 0)
    // error: ambiguous reference to member 'init(integerLiteral:)'
    }
    ```


    ## `_ExpressibleByBuiltinIntegerLiteral`

    For a numeric type modeling rational numbers, it is natural to conform that type to `ExpressibleByIntegerLiteral`. For instance, `let x = 42 as Rational` would create an instance of `Rational<Int>` with numerator `42` and denominator `1`. A similar line of reasoning applies to a numeric type modeling complex numbers (and likely other types as well).

    To conform to the protocol `ExpressibleByIntegerLiteral`, the type must implement an initializer of the form `init(integerLiteral: U)`. However, **`U` must conform to an underscored protocol `_ExpressibleByBuiltinIntegerLiteral`**.

    For a type such as `Rational<T>`, where the numerator and denominator are of type `T`, it is natural to have this initializer take an integer literal of type `T`. Currently, therefore, this requires that `T` be constrained to an underscored protocol. This is suboptimal for two reasons: (1) `_ExpressibleByBuiltinIntegerLiteral` is an underscored protocol not meant for public use; (2) it prevents an implementation of `Rational<T> where T : _ExpressibleByBuiltinIntegerLiteral` from having, say, arbitrary-precision integers as numerator and denominator (i.e., `Rational<BigInt>`) unless the arbitrary-precision integer type itself conforms to `_ExpressibleByBuiltinIntegerLiteral`.

    ### Suggestion for improvement

    From a user perspective, the semantics of `ExpressibleByIntegerLiteral` suggest that a generic type that has exclusively stored properties of type `T where T : ExpressibleByIntegerLiteral` should be conformable to `ExpressibleByIntegerLiteral` by implementing an initializer that accepts a literal value of type `T`--without being constrained to any underscored protocol. I suspect that this is not a trivial detail to implement in the compiler.


    ## Implementations of heterogeneous comparison and bit shift operators

    In Swift, there is a distinction between **default implementations** of protocol requirements and **protocol extension methods** which are not requirements. Both are defined in extensions to protocols, but default implementations are dynamically dispatched and can be overridden by conforming types, while extension methods can be shadowed but never overridden. (For example, homogeneous `==` is a protocol requirement of `Equatable`, but homogeneous `!=` is a protocol extension method that cannot be overridden.)

    The heterogeneous comparison and bit shift operators introduced in Swift 4.0 by SE-0104 are **protocol extension methods**.

    Consider a custom `BigInt` type and the comparison `(42 as BigInt) == (21 as UInt)`. There is an efficient way to perform that comparison which does not involve first converting the `UInt` value to a `BigInt` value. However, even if `BigInt` manually implements this specialized comparison operator, a generic algorithm implemented for all binary integers (say, for integer exponentiation) will use the less efficient standard library implementation of heterogeneous `==`. By contrast, the same algorithm will use the most specialized implementation of homogeneous `==`, because that function is a protocol requirement that is dynamically dispatched.

    ### Suggestion for improvement

    Heterogeneous `<`, `<=`, `==`, `>`, `>=` should be requirements of the protocol on which they are now extension methods, as should heterogeneous masking shifts. That would permit conforming types to provide more specialized implementations. This may, however, increase the work of the type checker at compile time.


    ## Semantics of masking shifts for arbitrary-precision integers

    SE-0104 introduced two different types of bit shift operations for integers, called **masking shifts** and **smart shifts**.

    Smart shifts, spelled `<<` and `>>`, are protocol extension methods that always behave in a well-defined way when overshifting or undershifting. For example, `x << -2` is now equivalent to `x >> 2` in Swift 4, where previously the behavior was undefined.

    However, because there is sometimes a performance cost for branches that handle overshifting and undershifting that cannot be optimized away, Swift now offers masking shifts, spelled `&<<` and `&>>`. As explained in SE-0104, "[a] masking shift logically preprocesses the right[-]hand operand by masking its bits to produce a value in the range `0...(x-1)` where `x` is the number of bits in the left[-]hand operand." These semantics for masking shifts make sense when the number of bits in the left-hand operand is fixed, as is the case for all fixed-width integers.

    However, all binary integers must implement `&<<` and `&>>`. For an arbitrary-width integer type (which, for various reasons, are best represented as sign-and-magnitude rather than two's-complement), the literal interpretation of those semantics is problematic. For example, most users might think that `(1 as BigInt) << 1000` should not result in overshift. However, the number of bits in the left-hand operand is 2, and therefore a plausible interpretation of the semantics of `&<<` would have `(1 as BigInt) &<< 1000 == 1`. By extension, therefore, `(1 as BigInt) << 1000 == 0`; this is a counterintuitive result.

    ### Suggestion for improvement

    The semantics of smart shift should be clarified to state that the right-hand operand is preprocessed by masking its bits to produce a value in the range `0..<x` where `x` is the **maximum** number of bits in a value of the same type as the left-hand operand. This is equivalent to viewing arbitrary-width integers _for the purposes of bitwise operations_ as notionally infinite sequences of bits sign-extended from the two's-complement representation of the integral value.

    For the purposes of implementation, `BinaryInteger` may require a new static property `maxBitWidth`, which would be equal to `bitWidth` for fixed-width integers and could be defined as `Int.max` for arbitrary-precision integers.


    ## `ArithmeticOverflow`

  11. xwu revised this gist Jun 17, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion user-experience-of-new-integer-protocols.md
    Original file line number Diff line number Diff line change
    @@ -23,7 +23,7 @@ The design, re-design, and implementation of [SE-0104][1], a proposal to revise

    SE-0104 added heterogeneous comparison and bit shift operators to the language to improve the user experience (for example, you can now check if an `Int` value is equal to a `UInt` value).<sup>[1](#fn1)</sup>

    > <sup id="fn1">1</sup> Similar enhancements for operations such as addition are not yet possible because a design is lacking for how to express _promotion_. When (if) Swift allows integer constants as generic parameters (e.g. `typealias Int64 = _Int<64>`), this may become a more realistic prospect as promotion might then be expressed as generic constraints (e.g. `func + <T, U>(lhs: T, rhs: U) -> U where T : FixedWidthInteger, U : FixedWidthInteger, T.BitWidth < U.BitWidth`). This is meant to be an aside and is certainly outside the scope of correcting or incrementally improving upon SE-0104.
    > <sup id="fn1">1</sup> Similar enhancements for operations such as addition are not yet possible because a design is lacking for how to express _promotion_. When (if) Swift allows integer constants in generic constraints, this may become a more realistic prospect as promotion would then be expressible using generic constraints (e.g. `func + <T, U>(lhs: T, rhs: U) -> U where T : FixedWidthInteger, U : FixedWidthInteger, T.BitWidth < U.BitWidth`). This is meant to be an aside and is certainly outside the scope of correcting or incrementally improving upon SE-0104.
    These operators behave as intended with concrete types, but comparisons in generic algorithms behave differently. This was encountered during review of the standard library's implementation of `DoubleWidth`, which in fact had a bug as a consequence of the following behavior:

  12. xwu revised this gist Jun 17, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion user-experience-of-new-integer-protocols.md
    Original file line number Diff line number Diff line change
    @@ -23,7 +23,7 @@ The design, re-design, and implementation of [SE-0104][1], a proposal to revise

    SE-0104 added heterogeneous comparison and bit shift operators to the language to improve the user experience (for example, you can now check if an `Int` value is equal to a `UInt` value).<sup>[1](#fn1)</sup>

    > <sup id="fn1">1</sup> Similar enhancements for operations such as addition are not yet possible because a design is lacking for how to express _promotion_. When (if) Swift allows integer constants as generic parameters (e.g. `typealias Int64 = _Int<64>`), this may become a more realistic prospect as promotion might then be expressed as generic constraints (e.g. `func + <T, U>(lhs: T, rhs: U) -> U where T : FixedWidthInteger, U : FixedWidthInteger, T.BitWidth < U.BitWidth`). This is meant to be an aside and is certainly outside the scope of correctly or incrementally improving upon SE-0104.
    > <sup id="fn1">1</sup> Similar enhancements for operations such as addition are not yet possible because a design is lacking for how to express _promotion_. When (if) Swift allows integer constants as generic parameters (e.g. `typealias Int64 = _Int<64>`), this may become a more realistic prospect as promotion might then be expressed as generic constraints (e.g. `func + <T, U>(lhs: T, rhs: U) -> U where T : FixedWidthInteger, U : FixedWidthInteger, T.BitWidth < U.BitWidth`). This is meant to be an aside and is certainly outside the scope of correcting or incrementally improving upon SE-0104.
    These operators behave as intended with concrete types, but comparisons in generic algorithms behave differently. This was encountered during review of the standard library's implementation of `DoubleWidth`, which in fact had a bug as a consequence of the following behavior:

  13. xwu revised this gist Jun 17, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion user-experience-of-new-integer-protocols.md
    Original file line number Diff line number Diff line change
    @@ -23,7 +23,7 @@ The design, re-design, and implementation of [SE-0104][1], a proposal to revise

    SE-0104 added heterogeneous comparison and bit shift operators to the language to improve the user experience (for example, you can now check if an `Int` value is equal to a `UInt` value).<sup>[1](#fn1)</sup>

    > <sup id="fn1">1</sup> Similar enhancements for operations such as addition are not yet possible because a design is lacking for how to express the idea of _promotion_. If and when Swift allows integer constants as generic parameters (e.g. `typealias Int64 = _Int<64>`), this may become a more realistic prospect as bit widths could then be expressed in generic constraints (e.g. `func + <T, U>(lhs: T, rhs: U) -> U where T : FixedWidthInteger, U : FixedWidthInteger, T.BitWidth < U.BitWidth`). This is meant to be an aside and is certainly outside the scope of correctly or incrementally improving upon SE-0104.
    > <sup id="fn1">1</sup> Similar enhancements for operations such as addition are not yet possible because a design is lacking for how to express _promotion_. When (if) Swift allows integer constants as generic parameters (e.g. `typealias Int64 = _Int<64>`), this may become a more realistic prospect as promotion might then be expressed as generic constraints (e.g. `func + <T, U>(lhs: T, rhs: U) -> U where T : FixedWidthInteger, U : FixedWidthInteger, T.BitWidth < U.BitWidth`). This is meant to be an aside and is certainly outside the scope of correctly or incrementally improving upon SE-0104.
    These operators behave as intended with concrete types, but comparisons in generic algorithms behave differently. This was encountered during review of the standard library's implementation of `DoubleWidth`, which in fact had a bug as a consequence of the following behavior:

  14. xwu revised this gist Jun 17, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion user-experience-of-new-integer-protocols.md
    Original file line number Diff line number Diff line change
    @@ -21,7 +21,7 @@ The design, re-design, and implementation of [SE-0104][1], a proposal to revise

    ## Heterogeneous comparison with integer literals

    SE-0104 added heterogeneous comparison and bit shift operators to the language to improve the user experience (for example, you can now check if an `Int` value is equal to a `UInt` value).<sup>(#fn1)</sup>
    SE-0104 added heterogeneous comparison and bit shift operators to the language to improve the user experience (for example, you can now check if an `Int` value is equal to a `UInt` value).<sup>[1](#fn1)</sup>

    > <sup id="fn1">1</sup> Similar enhancements for operations such as addition are not yet possible because a design is lacking for how to express the idea of _promotion_. If and when Swift allows integer constants as generic parameters (e.g. `typealias Int64 = _Int<64>`), this may become a more realistic prospect as bit widths could then be expressed in generic constraints (e.g. `func + <T, U>(lhs: T, rhs: U) -> U where T : FixedWidthInteger, U : FixedWidthInteger, T.BitWidth < U.BitWidth`). This is meant to be an aside and is certainly outside the scope of correctly or incrementally improving upon SE-0104.
  15. xwu revised this gist Jun 17, 2017. 1 changed file with 3 additions and 4 deletions.
    7 changes: 3 additions & 4 deletions user-experience-of-new-integer-protocols.md
    Original file line number Diff line number Diff line change
    @@ -21,7 +21,9 @@ The design, re-design, and implementation of [SE-0104][1], a proposal to revise

    ## Heterogeneous comparison with integer literals

    SE-0104 added heterogeneous comparison and bit shift operators to the language to improve the user experience (for example, you can now check if an `Int` value is equal to a `UInt` value).<sup id="a1">[1](#f1)</sup>
    SE-0104 added heterogeneous comparison and bit shift operators to the language to improve the user experience (for example, you can now check if an `Int` value is equal to a `UInt` value).<sup>(#fn1)</sup>

    > <sup id="fn1">1</sup> Similar enhancements for operations such as addition are not yet possible because a design is lacking for how to express the idea of _promotion_. If and when Swift allows integer constants as generic parameters (e.g. `typealias Int64 = _Int<64>`), this may become a more realistic prospect as bit widths could then be expressed in generic constraints (e.g. `func + <T, U>(lhs: T, rhs: U) -> U where T : FixedWidthInteger, U : FixedWidthInteger, T.BitWidth < U.BitWidth`). This is meant to be an aside and is certainly outside the scope of correctly or incrementally improving upon SE-0104.
    These operators behave as intended with concrete types, but comparisons in generic algorithms behave differently. This was encountered during review of the standard library's implementation of `DoubleWidth`, which in fact had a bug as a consequence of the following behavior:

    @@ -56,6 +58,3 @@ The reason that `h(_:)` gives a surprising result is explained as follows:

    Therefore, in the invocation `h(UInt.self)`, we are actually comparing `UInt.max` to `~(0 as Int)`. This is a surprising result, as evidenced by the fact that a bug nearly slipped into the standard library itself.


    <hr width="40"/>
    > <sup id="f1">1</sup> Similar enhancements for operations such as addition are not yet possible because a design is lacking for how to express the idea of _promotion_. If and when Swift allows integer constants as generic parameters (e.g. `typealias Int64 = _Int<64>`), this may become a more realistic prospect as bit widths could then be expressed in generic constraints (e.g. `func + <T, U>(lhs: T, rhs: U) -> U where T : FixedWidthInteger, U : FixedWidthInteger, T.BitWidth < U.BitWidth`). This is meant to be an aside and is certainly outside the scope of correctly or incrementally improving upon SE-0104. [](#a1)
  16. xwu revised this gist Jun 17, 2017. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions user-experience-of-new-integer-protocols.md
    Original file line number Diff line number Diff line change
    @@ -52,10 +52,10 @@ The reason that `h(_:)` gives a surprising result is explained as follows:

    * Each concrete integer type implements its own overload of the homogeneous comparison operator, whereas protocol extension methods implement heterogeneous comparison operators. When an integer literal is used on the right-hand side of the comparison, the compiler looks first to the concrete type for an suitable implementation of the operator and finds the homogeneous overload. Therefore, it does _not_ traverse the protocol hierarchy and instead infers the literal to be of the same type as the left-hand side.

    * In generic code, _even if_ the most refined protocol implements its own overload of the homogeneous comparison operator, the compiler will look for all overloads of the operator by traversing the entire protocol hierarchy. Therefore, it will always find an overload that accepts the "preferred" integer literal type (`Swift.IntegerLiteralType`, aka `Int`) and infers the literal to be of type `Int`.
    * In generic code, _even if_ the most refined protocol implements its own overload of the homogeneous comparison operator, the compiler will look for all overloads of the operator by traversing the entire protocol hierarchy. Since heterogeneous comparison operators are defined somewhere along the hierarchy, the will _always_ find an overload that accepts the "preferred" integer literal type (`Swift.IntegerLiteralType`, aka `Int`) and infers the literal to be of type `Int`.

    Therefore, in the invocation `h(UInt.self)`, we are actually comparing `UInt.max` to `~(0 as Int)`. This is clearly a surprising result.
    Therefore, in the invocation `h(UInt.self)`, we are actually comparing `UInt.max` to `~(0 as Int)`. This is a surprising result, as evidenced by the fact that a bug nearly slipped into the standard library itself.


    <hr width="20"/>
    <sup id="f1">1</sup> Similar enhancements for operations such as addition are not yet possible because a design is lacking for how to express the idea of _promotion_. If and when Swift allows integer constants as generic parameters (e.g. `typealias Int64 = _Int<64>`), this may become a more realistic prospect as bit widths could then be expressed in generic constraints (e.g. `func + <T, U>(lhs: T, rhs: U) -> U where T : FixedWidthInteger, U : FixedWidthInteger, T.BitWidth < U.BitWidth`). This is meant to be an aside and is certainly outside the scope of correctly or incrementally improving upon SE-0104. [](#a1)
    <hr width="40"/>
    > <sup id="f1">1</sup> Similar enhancements for operations such as addition are not yet possible because a design is lacking for how to express the idea of _promotion_. If and when Swift allows integer constants as generic parameters (e.g. `typealias Int64 = _Int<64>`), this may become a more realistic prospect as bit widths could then be expressed in generic constraints (e.g. `func + <T, U>(lhs: T, rhs: U) -> U where T : FixedWidthInteger, U : FixedWidthInteger, T.BitWidth < U.BitWidth`). This is meant to be an aside and is certainly outside the scope of correctly or incrementally improving upon SE-0104. [](#a1)
  17. xwu revised this gist Jun 17, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion user-experience-of-new-integer-protocols.md
    Original file line number Diff line number Diff line change
    @@ -57,5 +57,5 @@ The reason that `h(_:)` gives a surprising result is explained as follows:
    Therefore, in the invocation `h(UInt.self)`, we are actually comparing `UInt.max` to `~(0 as Int)`. This is clearly a surprising result.


    <hr width="20">
    <hr width="20"/>
    <sup id="f1">1</sup> Similar enhancements for operations such as addition are not yet possible because a design is lacking for how to express the idea of _promotion_. If and when Swift allows integer constants as generic parameters (e.g. `typealias Int64 = _Int<64>`), this may become a more realistic prospect as bit widths could then be expressed in generic constraints (e.g. `func + <T, U>(lhs: T, rhs: U) -> U where T : FixedWidthInteger, U : FixedWidthInteger, T.BitWidth < U.BitWidth`). This is meant to be an aside and is certainly outside the scope of correctly or incrementally improving upon SE-0104. [](#a1)
  18. xwu revised this gist Jun 17, 2017. 1 changed file with 14 additions and 2 deletions.
    16 changes: 14 additions & 2 deletions user-experience-of-new-integer-protocols.md
    Original file line number Diff line number Diff line change
    @@ -21,7 +21,9 @@ The design, re-design, and implementation of [SE-0104][1], a proposal to revise

    ## Heterogeneous comparison with integer literals

    SE-0104 added heterogeneous comparison and bit shift operators to the language, which improve the user experience <sup id="a1">[1](#f1)</sup>. However, it is possible
    SE-0104 added heterogeneous comparison and bit shift operators to the language to improve the user experience (for example, you can now check if an `Int` value is equal to a `UInt` value).<sup id="a1">[1](#f1)</sup>

    These operators behave as intended with concrete types, but comparisons in generic algorithms behave differently. This was encountered during review of the standard library's implementation of `DoubleWidth`, which in fact had a bug as a consequence of the following behavior:

    ```swift
    func f() -> Bool {
    @@ -46,4 +48,14 @@ h(UInt.self) // Returns `false`.
    i(UInt.self) // Returns `true`.
    ```

    <b id="f1">1</b>: Similar enhancements for operations such as addition are not yet possible because a design is lacking for how to express the idea of _promotion_. If and when Swift allows integer constants as generic parameters (e.g. `typealias Int64 = _Int<64>`), this may become a more realistic prospect as bit widths could then be expressed in generic constraints (e.g. `func + <T, U>(lhs: T, rhs: U) -> U where T : FixedWidthInteger, U : FixedWidthInteger, T.BitWidth < U.BitWidth`). This is meant entirely to be an aside and is certainly outside the scope of correctly or incrementally improving upon SE-0104. [](#a1)
    The reason that `h(_:)` gives a surprising result is explained as follows:

    * Each concrete integer type implements its own overload of the homogeneous comparison operator, whereas protocol extension methods implement heterogeneous comparison operators. When an integer literal is used on the right-hand side of the comparison, the compiler looks first to the concrete type for an suitable implementation of the operator and finds the homogeneous overload. Therefore, it does _not_ traverse the protocol hierarchy and instead infers the literal to be of the same type as the left-hand side.

    * In generic code, _even if_ the most refined protocol implements its own overload of the homogeneous comparison operator, the compiler will look for all overloads of the operator by traversing the entire protocol hierarchy. Therefore, it will always find an overload that accepts the "preferred" integer literal type (`Swift.IntegerLiteralType`, aka `Int`) and infers the literal to be of type `Int`.

    Therefore, in the invocation `h(UInt.self)`, we are actually comparing `UInt.max` to `~(0 as Int)`. This is clearly a surprising result.


    <hr width="20">
    <sup id="f1">1</sup> Similar enhancements for operations such as addition are not yet possible because a design is lacking for how to express the idea of _promotion_. If and when Swift allows integer constants as generic parameters (e.g. `typealias Int64 = _Int<64>`), this may become a more realistic prospect as bit widths could then be expressed in generic constraints (e.g. `func + <T, U>(lhs: T, rhs: U) -> U where T : FixedWidthInteger, U : FixedWidthInteger, T.BitWidth < U.BitWidth`). This is meant to be an aside and is certainly outside the scope of correctly or incrementally improving upon SE-0104. [](#a1)
  19. xwu revised this gist Jun 17, 2017. 1 changed file with 12 additions and 3 deletions.
    15 changes: 12 additions & 3 deletions user-experience-of-new-integer-protocols.md
    Original file line number Diff line number Diff line change
    @@ -12,14 +12,16 @@ The design, re-design, and implementation of [SE-0104][1], a proposal to revise

    ### Topics

    1. Performing generic heterogeneous comparison and integer literals
    1. Performing heterogeneous comparison with integer literals
    2. Conforming to `_ExpressibleByBuiltinIntegerLiteral`
    3. Overriding heterogeneous comparison and bit shift operators
    4. Masking shifts and arbitrary-width integers
    5. Using `ArithmeticOverflow`
    6. Initializing from a floating-point source

    ## Heterogeneous comparison
    ## Heterogeneous comparison with integer literals

    SE-0104 added heterogeneous comparison and bit shift operators to the language, which improve the user experience <sup id="a1">[1](#f1)</sup>. However, it is possible

    ```swift
    func f() -> Bool {
    @@ -37,4 +39,11 @@ func h<T : FixedWidthInteger>(_: T.Type) -> Bool {
    func i<T : FixedWidthInteger>(_: T.Type) -> Bool {
    return T.max == .max
    }
    ```

    f() // Returns `true`.
    g() // Returns `true`.
    h(UInt.self) // Returns `false`.
    i(UInt.self) // Returns `true`.
    ```

    <b id="f1">1</b>: Similar enhancements for operations such as addition are not yet possible because a design is lacking for how to express the idea of _promotion_. If and when Swift allows integer constants as generic parameters (e.g. `typealias Int64 = _Int<64>`), this may become a more realistic prospect as bit widths could then be expressed in generic constraints (e.g. `func + <T, U>(lhs: T, rhs: U) -> U where T : FixedWidthInteger, U : FixedWidthInteger, T.BitWidth < U.BitWidth`). This is meant entirely to be an aside and is certainly outside the scope of correctly or incrementally improving upon SE-0104. [](#a1)
  20. xwu revised this gist Jun 17, 2017. 1 changed file with 38 additions and 0 deletions.
    38 changes: 38 additions & 0 deletions user-experience-of-new-integer-protocols.md
    Original file line number Diff line number Diff line change
    @@ -1,2 +1,40 @@
    # Notes on the user experience of new integer protocols

    Xiaodi Wu
    June 17, 2017

    ## Introduction

    The design, re-design, and implementation of [SE-0104][1], a proposal to revise integer protocols in Swift, is now largely complete in shipping previews of Swift 4.0. As an exercise, I have used the new APIs to develop a [set of additional numeric facilities][2]. Here are some insights gained from that experience and suggestions for improvement based on that experience.

    [1]: https://github.com/apple/swift-evolution/blob/master/proposals/0104-improved-integers.md
    [2]: https://github.com/xwu/NumericAnnex

    ### Topics

    1. Performing generic heterogeneous comparison and integer literals
    2. Conforming to `_ExpressibleByBuiltinIntegerLiteral`
    3. Overriding heterogeneous comparison and bit shift operators
    4. Masking shifts and arbitrary-width integers
    5. Using `ArithmeticOverflow`
    6. Initializing from a floating-point source

    ## Heterogeneous comparison

    ```swift
    func f() -> Bool {
    return UInt.max == ~0
    }

    func g() -> Bool {
    return UInt.max == .max
    }

    func h<T : FixedWidthInteger>(_: T.Type) -> Bool {
    return T.max == ~0
    }

    func i<T : FixedWidthInteger>(_: T.Type) -> Bool {
    return T.max == .max
    }
    ```
  21. xwu created this gist Jun 17, 2017.
    2 changes: 2 additions & 0 deletions user-experience-of-new-integer-protocols.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,2 @@
    # Notes on the user experience of new integer protocols