> On Mar 30, 2017, at 2:56 PM, David Hart via swift-evolution 
> <[email protected]> wrote:
> 
> The current protocols ExpressibleByIntegerLiteral and 
> ExpressibleByFloatLiteral are simple and work well but don't support 
> arbitrary precision literal values. Replacing those protocols is a non-goal 
> as they provide a simple interface for work well for most cases.
> 
Honestly, I don't think I agree with this. I see no particular reason to like 
our current protocols; they break down as soon as your type gets larger than 
the largest standard library integer/float type, which undermines one of their 
main use cases.

I've been toying with a different approach in my head for a few weeks. The 
`BinaryInteger` protocol contains the concept of a `words` collection, which 
expresses any integer type as a collection of `UInt`s containing a signed 
two's-compliment representation of the integer. That means any `BinaryInteger` 
already contains code to handle a `words` collection. If we made this more 
exposed in some way, then `ExpressibleByIntegerLiteral` could leverage that 
conformance.

One approach would be to extract the `words` collection into a higher-level 
protocol:

        protocol BinaryIntegerSource {
                associatedtype Words: Collection where Iterator.Element == UInt
                var words: Words { get }
        }

Then we could modify `BinaryInteger` to accept this:

        protocol BinaryInteger: BinaryIntegerSource {
                ...
                init<T : BinaryIntegerSource>(_ source: T)
                ...
        }

And introduce a new `IntegerLiteral` type which is a `BinaryIntegerSource`, but 
not a `BinaryInteger` (so you can't do arithmetic with it):

        struct IntegerLiteral: BinaryIntegerSource {
                associatedtype Words = …
                var words: Words { … }
        }

And now, you can say something like:

        struct Int128: ExpressibleByIntegerLiteral {
                fileprivate var _value: DoubleWidth<Int64>
                
                init(integerLiteral value: IntegerLiteral) {
                        _value = DoubleWidth(value)
                }
        }

And everything ought to do what it's supposed to. You could still use a 
different type if you didn't need anything larger than, say, `Int`. I don't 
believe this would require any changes to the compiler; `IntegerLiteral` could 
conform to `_ExpressibleByBuiltinIntegerLiteral`, which would allow it to 
represent integers up to the current limit of 1024 bits + 1 sign bit.

(There are a few similar approaches we could take, like exposing an 
`init(words:)` constructor in `BinaryInteger` and having the `IntegerLiteral` 
behave as a `Words` collection, but all of them basically involve bootstrapping 
into `BinaryInteger` through the `Words` type.)

I *think* that the not-yet-implemented `BinaryFloatingPoint.init<Source: 
BinaryFloatingPoint>(_ value: Source)` initializers could be leveraged in a 
similar way—create a `BinaryFloatingPointSource` protocol and a 
`BinaryFloatLiteral` type that conforms to it—but I'm less certain of that 
because I don't really understand how this universal float conversion is 
supposed to work. Plus, the universal float conversion is still just a TODO 
comment right now.

(This would leave non-binary floats in the lurch, but we're pretty much doing 
that already—try initializing `Decimal` through its `ExpressibleByFloatLiteral` 
conformance sometime and you'll see what I mean. I would support changing its 
name to `ExpressibleByBinaryFloatLiteral`.)

These leave our current integer and floating-point literal size limits 
(1025-bit signed integers and 80-bit floats) in place, but those are 
implementation details and could be changed. In practice, I very much hope the 
compiler will try to optimize initialization from literals aggressively.

-- 
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to