<div dir="ltr">On Sat, Jan 14, 2017 at 1:32 AM, Jacob Bandes-Storch via swift-evolution <span dir="ltr"><<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>></span> wrote:<br><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><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/<wbr>jtbandes/<wbr>93eeb7d5eee8e1a7245387c660d53b<wbr>03#file-affine-swift-L16-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></div></blockquote><div><br></div><div>I think, in the end, it's the _name_ that could use improvement here. As the doc comments say, `Arithmetic` is supposed to provide a "suitable basis for arithmetic on scalars"--perhaps `ScalarArithmetic` might be more appropriate? It would make it clear that `CGVector` is not meant to be a conforming type.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div><div></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.</div></div></div></blockquote><div><br></div><div>Here (just IMHO), I disagree. Since any binary integer can be truncated and any can be right-shifted, it makes sense for `init(extendingOrTruncating:)` to be available for all of them. If I understand the proposal correctly, `Int(extendingOrTruncating: (-1 as Int8))` would give you a different result than `Int(extendingOrTruncating: (255 as UInt8)`.</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div><div></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></div></blockquote><div><br></div><div>I would disagree with that as well; the existing `init(_:)` truncates the fractional part but errors if that value is outside the representable range, while the word "truncating" makes it sound like something is done to make any possible source value give you a destination value (a la "extendingOrTruncating" for integers).</div><div><br></div><div>Meanwhile, "nearest to" is problematic for me because either 127 and 129 is "nearest to" 128, and neither integer is "nearest to" 500, yet `Int8(clamping: 128)` and `Int8(clamping: 500)` both give you 127. This operation is very different from lround() for floating point, which in Swift is `rounded(.toNearestOrAwayFromZero)` (or just `rounded()`).</div><div><br></div><div>In both these cases, I think it's important to use spellings that distinguish doing things to the fractional part of floating point values and doing things to the binary representation of integer values. I think there's great value in using the term "clamp", which is very different from "nearest"; and in using an unlabeled `init(_:)` for initializing from FP values, which is most similar to the unlabeled `init(_:)` for initializing from integer values, as opposed to your suggested `init(truncating:)` which connotes some similarity to `init(extendingOrTruncating:)` for integer values.</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><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></div></blockquote><div><br></div><div>You can perfectly legitimately get a popcount for a signed integer. It's just looking at the binary representation and counting the ones. But then with two's complement, it'd have to be restricted to FixedWidthInteger and not BinaryInteger, because the same negative value would have a different popcount depending on the type's bitwidth. I'd disagree strongly with removing popcount from signed binary integers. However, I suppose the same requirement could be applied to both FixedWidthInteger and UnsignedInteger.</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><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="m_-2176513483445216513gmail-m_9191639816149247006m_409108456507017154gmail-m_-421357865757461754gmail_signature"><div dir="ltr"><div>Jacob<br></div></div></div></div><span class="">
<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></span></div></div></div></div>
<br>______________________________<wbr>_________________<br>
swift-evolution mailing list<br>
<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/<wbr>mailman/listinfo/swift-<wbr>evolution</a><br>
<br></blockquote></div><br></div></div>