<div dir="ltr">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:<div><div><br></div><div><b>## Arithmetic</b></div><div><br></div><div><div>Why are ExpressibleByIntegerLiteral and init?<T:BinaryInteger>(exactly<wbr>:) 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.</div></div><div><br></div><div><br></div><div>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:</div><div><br></div><div><font face="monospace, monospace"> func lerp<T: Arithmetic>(from a: T, to b: T, by ratio: T) -> T {</font></div><div><font face="monospace, monospace"> return a + (b - a) * ratio</font></div><div><font face="monospace, monospace"> }</font><br></div><div><br></div><div>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, <b>CGFloat</b>) and /(CGVector, <b>CGFloat</b>).</div><div><br></div><div>After consulting a mathematician, I believe what the lerp function really wants is for its generic param to be an <a href="https://en.wikipedia.org/wiki/Affine_space" target="_blank">affine space</a>. I explored this a bit here: <a href="https://gist.github.com/jtbandes/93eeb7d5eee8e1a7245387c660d53b03#file-affine-swift-L16-L18" target="_blank">https://gist.github.com/jtband<wbr>es/93eeb7d5eee8e1a7245387c660d<wbr>53b03#file-affine-swift-L16-<wbr>L18</a></div><div><br></div><div>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:</div><div><br></div><div><font face="monospace, monospace"> extension Arithmetic: AffineSpace, VectorSpace { // not actually allowed in Swift</font></div><div><font face="monospace, monospace"> typealias Displacement = Self</font></div><div><font face="monospace, monospace"> typealias Scalar = Self</font></div><div><font face="monospace, monospace"> }</font></div><div><br></div><div>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...</div><div><br></div><div><b><br></b></div><div><b>## BinaryInteger</b></div><div><br></div><div>I'm a little confused by the presence of init(extendingOrTruncating:) for <i>all</i> 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.<br></div><div><br></div><div>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.<br></div><div><br></div><div><b>... masking shifts</b></div><div><br></div><div>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.</div><div><br></div><div>(Minor: were the smart shift operators supposed to be included as BinaryInteger protocol requirements? I only see them in the "heterogeneous shifts" extension.)</div><div><br></div><div><b>... init<T: BinaryInteger>(_ source: T)</b></div><div><div><br></div><div>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>.<wbr>size), which is pretty ugly.</div><div><br></div></div><div><br></div><div><b>## FixedWidthInteger</b></div><div><br></div><div>Why is popcount restricted to FixedWidthInteger? It seems like it could theoretically apply to any UnsignedInteger.</div><div><br></div><div><br></div><div><b>## Heterogenous shifts, equality, and comparison</b></div><div><br></div><div>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:</div><div><br></div><div><font face="monospace, monospace"> static func &>> <Other : BinaryInteger>(lhs: Self, rhs: Other) -> Self {</font></div><div><font face="monospace, monospace"> // delegate to the protocol requirement &>>(Self, Self)<br></font></div><div><font face="monospace, monospace"> return self &>> Self(extendingOrTruncating: rhs)</font></div><div><font face="monospace, monospace"> }</font></div><div><br></div><div>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:)...</div><div><br></div><div>Also, should these be protocol requirements so user-defined types can specialize them?</div><div><br></div><div><br></div><div><b>## Masking arithmetic</b><br></div><div><br></div><div>Do &* and &+ and &- need their operands to have the same type, or could these be heterogeneous too (at least &+ and &-)?</div><div><br></div><div><br></div><div><div class="gmail_extra"><div><div class="gmail-m_9191639816149247006m_409108456507017154gmail-m_-421357865757461754gmail_signature"><div dir="ltr"><div>Jacob<br></div></div></div></div>
<br><div class="gmail_quote">On Fri, Jan 13, 2017 at 12:47 PM, Max Moiseev via swift-evolution <span dir="ltr"><<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word">Hi everyone,<div><br></div><div>Back in June 2016 we discussed the new design of the integer types for the standard library. It even resulted in acceptance of <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0104-improved-integers.md" target="_blank">SE-0104</a> for Swift 3. Unfortunately we were not able to implement it in time for the release.</div><div><br></div><div>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.</div><div>Without further introduction, please welcome the refined proposal to make integers in Swift more suitable for generic programming.</div><div><br></div><div>Available in this gist <a href="https://gist.github.com/moiseev/62ffe3c91b66866fdebf6f3fcc7cad8c" target="_blank">https://gist.github.com/m<wbr>oiseev/62ffe3c91b66866fdebf6f3<wbr>fcc7cad8c</a> and also inlined below.</div><div><br></div><div>Max</div></div></blockquote></div></div></div></div></div>