[swift-evolution] Strings in Swift 4

Dave Abrahams dabrahams at apple.com
Sat Feb 4 16:27:30 CST 2017


on Sat Feb 04 2017, Jonathan Hull <swift-evolution at swift.org> wrote:

>> On Feb 2, 2017, at 2:19 PM, Dave Abrahams <dabrahams at apple.com> wrote:
>> 
>> 
>> on Thu Feb 02 2017, Jonathan Hull <jhull-AT-gbis.com> wrote:
>> 
>>> Just out of curiosity, what are the use-cases for an infinite sequence
>>> (as opposed to a sequence which is bounded to the type’s representable
>>> values)?
>> 
>> 1. The type may not have an inherent expressible bound (see BigInt,
>>   UnsafePointer, and *many* real-life Index types).
>
> If I understand what you are saying, this is why I was arguing that
> these partial ranges should not, by themselves, conform to Sequence.
> In cases where you do have a natural expressible bound, then it should
> conditionally conform to sequence.  

I'm sorry, I don't understand that last sentence.  The only way to
conditionally conform in one particular case is to make the conformance
condition include detection of that case. I could guess at what you mean
but I'd rather you explain yourself.  Specifically, which partial ranges
should conform to Sequence, and which shouldn't, in your view?

> In other cases, you should have to write that conformance yourself if
> you want it.

You've asserted that things should be a certain way (which I don't fully
understand but I hope you'll explain), but you haven't said anything to
clarify *why* you think they should be that way.

>> 2. I keep repeating variants of this example:
>> 
>>  func listElements<
>>    S: Sequence, N: Number
>>> (of s: S, numberedFrom start: N) {
>>    for (n, e) in zip(start..., s) {
>>      print("\(n). \(e)")
>>    }
>>  }
>> 
>>  which avoids incorrect behavior when N turns out to be a type that
>>  can't represent values high enough to list everything in s—**if and
>>  only if** `start...` is an unbounded range, rather than one that
>>  implicitly gets its upper bound from its type.
>
> I really worry about the possibility of long-term foot-gunning in this
> case.  I showed this to a friend who maintains old/abandoned codebases
> for a living, and he said “Oh god, that isn’t going to actually crash
> until 5 years in, when it gets passed some rare type of file… and by
> then the original programmer will have left.”  

Then either he's using the wrong language, or he needs to come to grips
with the realities of software that's both reliable and efficient.
Swift explicitly acknowledges that in efficient code there are inherent
representational limitations that force a choice between stopping the
program and continuing with incorrect behavior.  Swift explicitly
chooses to stop the program in these cases.  Practically speaking, every
program written in Swift is full of checks for these conditions, and
this is nothing new.

> He hit on exactly what I had been feeling as well (but hadn’t been
> able to put into words until I heard his).  The thought is that this
> will help the programmer find an error by trapping, 

It will also prevent a program from wandering off into
unpredictable/broken behavior in the hands of a user.  If your software
keeps running but writes a corrupted file, that's usually much worse
than a hard stop.

> but because it is dependent on the interactions of two subsystems, it
> will often create one of those crashes where the stars have to align
> for it to happen (which are my least favorite type of bug/crash to
> debug).

You could look at it that way, but even in a programming language such
as Python, whose integers are effectively all BigInts, a similar
“stars-have-to-align” scenario occurs when you run out of memory.  On
modern opeerating systems that effectively means your whole system
grinds to a halt. It is no better than a crash, and often leads to a
crash (sorry, you have no stack space left with which to make this
function call!)  These things are a fact of life.

> I think this example also shows how my suggestion of requiring an
> extra protocol for conformance to Sequence would actually be much more
> effective in preventing the programmer error…
>
> You would not have been able to write the function the way you did,
> because writing ‘start…’ as a sequence would have required the
> addition of ‘where N:FiniteComparable’ (or whatever we call the
> protocol providing natural bounds).  In having to write that N needs
> bounds, you are much more likely to be thinking of what those bounds
> might be.  

I explicitly *don't want* N to need to have expressible bounds.  When we
get a BigInt type, it will be especially effective in these scenarios.

> The problem here is really with the design of the listElements
> function in allowing/encouraging that error, as opposed to the
> caller... and the trap is more likely to make you look at the call
> site because of the context of when it happens. 

I think if you diligently apply that line of reasoning to large systems,
you'll quickly find that writing them becomes impossible ;-)

> Having to write the ‘where’ statement makes you reconsider the design
> of the function, again because of the context of when it happens.

-- 
-Dave



More information about the swift-evolution mailing list