[swift-evolution] protocol-oriented integers (take 2)

Jacob Bandes-Storch jtbandes at gmail.com
Sat Jan 14 01:32:03 CST 2017


Great work, all. I'm not sure I ever commented on SE-0104, so I've read
through this one more carefully. Here are some things that came to mind:

*## Arithmetic*

Why are ExpressibleByIntegerLiteral and init?<T:BinaryInteger>(exactly:)
required? I could understand needing access to 0, but this could be
provided by a static property or nullary initializer. It doesn't seem like
all types supporting arithmetic operations would necessarily be convertible
from an arbitrary binary integer.


I tried to evaluate the Arithmetic protocol by considering what it means
for higher-dimensional types such as CGPoint and CGVector. My use case was
a linear interpolation function:

    func lerp<T: Arithmetic>(from a: T, to b: T, by ratio: T) -> T {
        return a + (b - a) * ratio
    }

If I've read the proposal correctly, this definition works for integer and
floating-point values. But we can't make it work properly for CGVector (or,
perhaps less mathematically correct but more familiar, CGPoint). It's okay
to define +(CGVector, CGVector) and -(CGVector, CGVector), but *(CGVector,
CGVector) and /(CGVector, CGVector) don't make much sense. What we really
want is *(CGVector, *CGFloat*) and /(CGVector, *CGFloat*).

After consulting a mathematician, I believe what the lerp function really
wants is for its generic param to be an affine space
<https://en.wikipedia.org/wiki/Affine_space>. I explored this a bit here:
https://gist.github.com/jtbandes/93eeb7d5eee8e1a7245387c660d
53b03#file-affine-swift-L16-L18

This approach is less restrictive and more composable than the proposed
Arithmetic protocol, which can be viewed as a special case with the
following definitions:

    extension Arithmetic: AffineSpace, VectorSpace {  // not actually
allowed in Swift
        typealias Displacement = Self
        typealias Scalar = Self
    }

It'd be great to be able to define a lerp() which works for all
floating-point and integer numbers, as well as points and vectors (assuming
a glorious future where CGPoint and CGVector have built-in arithmetic
operations). But maybe the complexity of these extra protocols isn't worth
it for the stdlib...


*## BinaryInteger*

I'm a little confused by the presence of init(extendingOrTruncating:) for
*all* binary integers. Why does it make sense to be able to write
UInt16(extendingOrTruncating: (-21 as Int8)) ? In my mind, sign-extension
only has meaning when the source and destination types are both signed.

Although I would not be against a clamp() function in the standard library,
"init(clamping:)" sounds strange to me. What about calling it
"init(nearestTo:)"?  One could also define a FloatingPoint version of
init(nearestTo:), i.e. lround(). For maximum annoyance and explicitness,
you could even rename the existing init(_:) to init(truncating:) to make it
clear that truncation, not regular rounding, occurs when converting from
floating-point.

*... masking shifts*

The example claims that "(30 as UInt8) &>> 11" produces 3, because it
right-shifts 30 by 3. But isn't the bitWidth of UInt8 always 8 since it's a
fixed-width type? Why should 11 get masked to 3 before the shift? (Also, it
might be a good idea to choose examples with numbers whose base-ten
representations don't look like valid binary. 😉) What use cases are there
for masking shifts? I was under the impression that "smart shifts" were
pretty much how the usual shift instructions already behaved.

(Minor: were the smart shift operators supposed to be included as
BinaryInteger protocol requirements? I only see them in the "heterogeneous
shifts" extension.)

*... init<T: BinaryInteger>(_ source: T)*

Now a thought experiment: suppose you wanted to write an
arbitrary-precision BigInt, or any binary integer such as Int256. The
BinaryInteger protocol requires you to provide init<T:BinaryInteger>(_
source: T). Given the source of type T, how do you access its bits? Is
repeated use of word(at:) the recommended way? If so, it might be nice to
include a "wordCount" returning the number of available words; otherwise I
suppose the user has to use something like bitWidth/(8*MemoryLayout<Int>.size),
which is pretty ugly.


*## FixedWidthInteger*

Why is popcount restricted to FixedWidthInteger? It seems like it could
theoretically apply to any UnsignedInteger.


*## Heterogenous shifts, equality, and comparison*

These look great. How will the default implementations be provided?
(Equivalent question: how would a user define their own heterogeneous
operators?) I suppose this works:

    static func &>> <Other : BinaryInteger>(lhs: Self, rhs: Other) -> Self {
        // delegate to the protocol requirement &>>(Self, Self)
        return self &>> Self(extendingOrTruncating: rhs)
    }

But for operations you can't delegate so easily... I'm imagining trying to
implement heterogeneous comparison (i.e. < ) and the best I can come up
with uses signum() and word(at:)...

Also, should these be protocol requirements so user-defined types can
specialize them?


*## Masking arithmetic*

Do &* and &+ and &- need their operands to have the same type, or could
these be heterogeneous too (at least &+ and &-)?


Jacob

On Fri, Jan 13, 2017 at 12:47 PM, Max Moiseev via swift-evolution <
swift-evolution at swift.org> wrote:

> Hi everyone,
>
> Back in June 2016 we discussed the new design of the integer types for the
> standard library. It even resulted in acceptance of SE-0104
> <https://github.com/apple/swift-evolution/blob/master/proposals/0104-improved-integers.md> for
> Swift 3. Unfortunately we were not able to implement it in time for the
> release.
>
> But it was not forgotten, although, as time went by, a few changes needed
> to be made in order to reflect the current state of the language.
> Without further introduction, please welcome the refined proposal to make
> integers in Swift more suitable for generic programming.
>
> Available in this gist https://gist.github.com/m
> oiseev/62ffe3c91b66866fdebf6f3fcc7cad8c and also inlined below.
>
> Max
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170113/8c31a49e/attachment.html>


More information about the swift-evolution mailing list