[swift-evolution] [swift-evolution-announce] [Review] SE-0104: Protocol-oriented integers

Jordan Rose jordan_rose at apple.com
Thu Jun 23 17:53:00 CDT 2016

> On Jun 23, 2016, at 15:35, Max Moiseev <moiseev at apple.com> wrote:
> Hi Jordan,
>> On Jun 23, 2016, at 1:50 PM, Jordan Rose <jordan_rose at apple.com <mailto:jordan_rose at apple.com>> wrote:
>> Hey, standard library folks. Glad we're doing this one. :-)
>> - I remain unconvinced that defining an Arithmetic that includes both exact and floating-point numbers is a good idea. All of the arguments from Swift 1 and 2 about why we didn't include this still seem relevant. To phrase it in generic programming terms, what algorithm would be generic over Arithmetic?
> Steve, I don’t remember exactly why we chose to do it this time. Can you answer?
>> - What is Integer.init<T: FloatingPoint>(_:) supposed to do if the floating-point value is larger than the maximum representable integer? Smaller than the minimum? (As a special case, negative, when the integer type is unsigned?) Infinity? NaN?
> The same thing it does now — trap.

I think this is worth calling out as a precondition: the value must be within the bounds of the integer type. (The implementation may not trap for some values slightly outside the integer type, like 0.1 being converted to UInt, but it's still not defined.)

>> - Integer.init<T: Integer>(_:) currently says "if it is representable". It should say something like "trapping if it is not representable”.
> There is a comment saying that this is the precondition that must be checked by the caller. The initializer will trap but this is the implementation detail, isn’t it?

Ah, of course. My bad.

>> - I find it odd that Integer.init(clamping:) privileges the bounds of fixed-width integers. I was going to suggest it should take a range to clamp to that defaults to the min and max, but that's not implementable for a BigInt.
> It is always possible to pass bounds as an optional value and default to min…max in .none case.
> So you are suggesting something like `init<T : Integer>(clamping: T, within bounds: Optional<ClosedRange<Self>> = nil)` then?
> I don’t disagree. Looks useful, but I cannot imagine a good real world use for it, except for:
> extension Integer {
>   public func findClosest(to: Self, within bounds: ClosedRange<Self>) {
>     return Self(clamping: to, within: bounds)
>   }
> }

I don't find it any different from the existing requirement. If I'm going from a potentially large (absolute) value to a smaller one, it's possible I only care about representation, but it's also possible I have a smaller type here because of context.

englishPluralIndex = items.count.clamped(to: 0...2)

Maybe I'd go the other way, and say that this isn't a useful requirement. Are there algorithms that need this that are generic over Integer? (rather than FixedWidthInteger)

>> - nthWord should count "from least-significant to most-significant" rather than "from the right”.
> Will this definition work fine with different endiannesses? It needs some thinking, but I see your point.

Yes, "significant" always refers to place values, not to the representation in memory/registers.

>> - Why is bitWidth in bits but nthWord in words? (I know there's a good answer to this, but using them together seems like it will be common.)
> There is a derived property that will return the width in words based on bitWidth and word size.

Got it. May be deserving of a SeeAlso.

>> - Why are bitwise operations limited to fixed-width integers? I see "The only difference is that because shifting left truncates the high bits of fixed-width integers, it is hard to define what a left shift would mean to an arbitrary-precision integer" further down, but I would just assume it wouldn't truncate (i.e. it would be a pure multiplication by two).
> Exactly. It won’t truncate and we considered it to be a sufficient difference in behavior to not allow it to be used in the context over all integers.

Hm. I guess that makes sense. My interpretation: the behavior on fixed-width integers is well-understood, the behavior on arbitrary-precision integers is easy to understand, but the behavior in an algorithm generic over both might be problematic. Thanks for the explanation.

>> - Is there a requirement about left-shifting into the sign bit, for '<<' and for '&<<‘?
> The current behavior is to not do anything special about the sign bit. So that (64 as Int8) << 1 would result in a -128.
> I should probably add a general note somewhere in the proposal that “unless specifically mentioned, the behavior will remain consistent with the existing implementation”.

Sure. In this case I was asking about the requirement, though. If I use something like llvm::APSInt, which has an arbitrary-but-fixed-at-creation number of words, does shifting into the sign bit of a signed integer always result in a negative number? It never traps or is assumed not to happen?

>> - What is the ArithmeticOverflow type?
> It is an enum with 2 cases, just like Optional<()>, but with more specific case name. If not for the explicitness, a simple Bool value could have been used.

Please do include this in the proposal. :-)

>> - When does the remainder operation overflow? (I just can't remember.)
> Discussed in a separate email. The only case where it should trap is ‘division by zero’, so this part is subjected to changes.
>> - I feel a little weird having "someValue.and(mask)". Maybe bitwiseAnd or bitwiseAND to be more explicit?
> Doesn’t return type hint at the ‘bitwise' and not logical nature of these operations? Besides, I would expect operators to be used instead of actual protocol functions.

I guess so. It just reads oddly to me.

>> - maskingShiftLeft/Right seem underspecified in their doc comments. Why can't the protocol requirement just assume the shift amount has already been masked, instead of performing the masking themselves? Is it because we won't be able to optimize that away?
> There is a section about shifts: https://github.com/apple/swift-evolution/blob/master/proposals/0104-improved-integers.md#a-note-on-bit-shifts <https://github.com/apple/swift-evolution/blob/master/proposals/0104-improved-integers.md#a-note-on-bit-shifts>
> Let me know if you think it is insufficient.

I did see this section, but that's about the operator-based interface, not the requirements on a model type. I'd like to hear a little more about what the expected input range of maskingShiftLeft is and why.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160623/c09e187f/attachment.html>

More information about the swift-evolution mailing list