[swift-evolution] Implicit truncation

Xiaodi Wu xiaodi.wu at gmail.com
Tue May 23 15:07:50 CDT 2017

On Tue, May 23, 2017 at 5:36 AM, Haravikk <swift-evolution at haravikk.me>

> This is getting too muddled, so I'm going to summarise thusly; I believe
> the principle the OP raised is sound, that any initialiser performing a
> lossy conversion from another type should be clear that this happening, and
> that a label can do that best, as it makes the conversion self-documenting
> at the call-site.

This is a much wider claim that any advanced in the conversation
previously. Swift documentation refers to `init(_:)` as the "default
initializer." If I understand you, you are arguing that _any default
initializer must be value-preserving (monomorphic)_. This is plainly not
the current standard. For example, the following is a default conversion
but not a monomorphic conversion because multiple different arrays yield
the same resulting set:

let x = Set([1, 2, 3, 4, 3, 2, 1])

// x: Set<Int> = {
//  [0] = 2
//  [1] = 3
//  [2] = 1
//  [3] = 4
// }

Much has been quoted from the Swift API guidelines. The language in that
document is fairly terse: it gives guidelines in imperative form (e.g.,
"Omit useless words") with some follow-on suggestions worded more softly.
The strong guideline is that value-preserving (monomorphic) initializers
should omit the label. Notably, it is _not_ the other way around: that is,
it does _not_ require that all initializers omitting the label are
value-preserving. Now, there _is_ a "recommend[ation]" to distinguish
non-value-preserving initializers by a label. During the discussion on this
list about that recommendation, IIRC, the gist was that this is geared
toward situations where some conversions from type A to type B are lossy
and others are not; in that case, a label should be provided for the lossy
conversion(s) so that users don't use it when they mean to use the lossless

In the example of array-to-set conversion, there is _no possible
non-failable monomorphic conversion_ from array to set; however, there is
an accepted default lossy way to make the conversion, and therefore the
default initializer adopts that behavior and does not need to announce that
it is lossy. Likewise, there is _no possible non-failable monomorphic
conversion_ from floating point to integer; however, there is an accepted
default lossy way to make the conversion (it is an LLVM intrinsic), and
therefore the default initializer adopts that behavior and does not need to
announce that it is lossy.

Now, if you want to argue that you in particular do not accept that the
LLVM intrinsic is the default lossy way to convert from floating point to
integer, or that it is highly confusing, here's the place for that
discussion. But you're now arguing (IIUC) that regardless of how accepted
the default behavior is, if it is not lossless then it must be spelled with
a label, and that is plainly not the current convention in Swift.

Consider for example; with type-inference it's not always obvious what the
> type of a variable is, and an unlabelled initialiser does nothing to help,
> like-so:
> var someValue = someMethod()
>> var someInt = Int(someValue)
> At a glance it's not all obvious what is happening to someValue here; in
> fact I'd argue that this looks like a lossless conversion, requiring you to
> find out that someMethod() returns a Float before you can know for sure
> what's really going on.

Again, this is based on the claim that _only_ lossless conversions use
unlabeled initializers. You express the opinion that it _should_ be the
case above, but as I have already replied, it is factually _not_ the case
currently. That is, for an arbitrary pair of types A and B:

let x = A()
let y = B(x) // There is _no guarantee_ that this is lossless.

Whereas the following is more clear:
> var someValue = someMethod()
>> var someInt = Int(truncating: someValue)
> It may not communicate everything that's happening, but at least now it's
> clear at a glance that *something* is happening, and the term truncating
> suggests that something is being lost/removed from someValue.
> Now I don't really care if truncating is the best term for it or not,
> though I do still think it is and won't change my mind on that;

I think we're done here, then. What is the point of having a discussion
with reasoned arguments if you're pre-committed to not changing your mind?

> the key thing here is that it's providing that extra safety to developers
> by making it clear that an important conversion is taking place, and this
> to me is more consistent with other initialisers on Int etc. where
> distinctions are made between types that can and cannot be represented
> exactly.
> I'm not saying it should be limited to floats either; I think any
> initialiser that cannot, or may not, represent the passed value accurately
> should be labelled for clarity.

If you want to make that argument, it is a different discussion from the
one here about integer math and floating point. You'd be re-opening a
discussion on the Swift API naming guidelines and asking for a much wider
re-examination of the entire Swift API surface.

> So passing an Int16 into Int32(_:) would be fine for example, but the
> reverse should not be.
> It's not just an issue of new developers; experienced ones make mistakes
> too, or forget to consider whether the conversion will impact their code. A
> label helps, though I still think forcing a decision on rounding is even
> better, as both prevent a developer from simply throwing in a value in a
> way that may be a mistake.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170523/08b77bb3/attachment.html>

More information about the swift-evolution mailing list