[swift-evolution] [Discussion] Arbitrary precision integer and float literal protocols

David Hart david at hartbit.com
Mon Apr 3 01:19:10 CDT 2017



>> On 31 Mar 2017, at 10:45, Brent Royal-Gordon <brent at architechies.com> wrote:
>> 
>> On Mar 30, 2017, at 2:56 PM, David Hart via swift-evolution <swift-evolution at swift.org> 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.

The two major reason I see is their simplicity and their type safety.

Simplicity 
I recently conformed a type to both protocols and could not support arbitrary precision because the backing type was CGFloat. It would have been less obvious how to implement those conformances with the arbitrary precision initializers. I can easily see another developer be confused by them. For me, they are specialty initializers.

Type-safety
The protocols currently in the Standard Library allow you to implement them using any valid built-in type, providing type-safety: if you try to initialize a conforming type with a literal value outside the bounds of the picked built-in type, you will get a compiler error.

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

Looks like a nice solution! It would allow us to keep one ExpressibleBy* protocol.

Any reason we need to burden ourselves with the two's complement representation?

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

What do you mean by the universal float conversion?

> (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
> 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170403/294758a2/attachment.html>


More information about the swift-evolution mailing list