<div dir="ltr"><div>Hi Swift developers,<br></div><div>I hereby present my first Swift proposal regarding the <span style="color:rgb(112,61,170);font-family:Menlo;font-size:11px">endIndex</span> on `Collections` and closed `Ranges`. I’ve searched the mailing list archives for something similar to this but couldn’t find it, so I decided to come forward.</div><div><br></div><div><b>The problem:</b></div><div>I was recently working on a library and used a closed <span style="color:rgb(112,61,170);font-family:Menlo;font-size:11px">Range</span> to define the bounds of a board game and the tests that used its <span style="color:rgb(112,61,170);font-family:Menlo;font-size:11px">endIndex</span> were all failing. Started debugging it around and, to my complete surprise, found out that the <span style="color:rgb(112,61,170);font-family:Menlo;font-size:11px">endIndex</span> of the closed <span style="color:rgb(112,61,170);font-family:Menlo;font-size:11px">Ranges</span> was always +1 from the value I initially set. With this, I decided to dive into the <span style="color:rgb(112,61,170);font-family:Menlo;font-size:11px">Range</span> source code and discovered that all closed ranges are converted to half-open ones when initialized:</div><div>a) <span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">1</span><span style="font-family:Menlo;font-size:11px">..<</span><span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">10</span> stays <span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">1</span><span style="font-family:Menlo;font-size:11px">..<</span><span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">10</span> (<span style="color:rgb(112,61,170);font-family:Menlo;font-size:11px">endIndex</span> = 10)<br></div><div>b) <span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">1</span><span style="font-family:Menlo;font-size:11px">...</span><span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">10</span> is converted to <span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">1</span><span style="font-family:Menlo;font-size:11px">..<</span><span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">11</span> (<span style="color:rgb(112,61,170);font-family:Menlo;font-size:11px">endIndex</span> = 11)<br></div><div><br></div><div>To work around this behavior I had to subtract 1 every time I checked for the <span style="color:rgb(112,61,170);font-family:Menlo;font-size:11px">endIndex</span>, since I didn't want to use <span style="color:rgb(112,61,170);font-family:Menlo;font-size:11px">last</span><span style="color:rgb(0,0,0);font-family:Menlo;font-size:11px">!</span> (force unwrapping) or if-let, which ultimately polluted my code. You could argue that changing from <span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">1</span><span style="font-family:Menlo;font-size:11px">...</span><span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">10</span> to <span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">1</span><span style="font-family:Menlo;font-size:11px">...</span><span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">9</span> would get rid of all of this, since it gets translated to <span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">1</span><span style="font-family:Menlo;font-size:11px">..<</span><span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">10</span> with <span style="color:rgb(112,61,170);font-family:Menlo;font-size:11px">endIndex</span> being 10 (which is the value I expect), but I think it’s just not worth it to “obscure” that way.</div><div><br></div><div>I’ve asked some fellow developer friends their thoughts having the value 11 when accessing <span style="color:rgb(112,61,170);font-family:Menlo;font-size:11px">endIndex</span> on b) and a lot of them were confused and did not expect the outcome, and I totally agree, it’s not intuitive at all. To me, <span style="color:rgb(112,61,170);font-family:Menlo;font-size:11px">endIndex</span> implies that the returned value is the last valid and accessible index of a Collection, not its size/count or any other value that is outside the collection’s bounds.</div><div><br></div><div><b>The solution:</b></div><div>Add an optional boolean parameter, `closed`, to the <span style="color:rgb(112,61,170);font-family:Menlo;font-size:11px">Range</span> <span style="color:rgb(187,44,162);font-family:Menlo;font-size:11px">init</span> method with the default value of <span style="color:rgb(187,44,162);font-family:Menlo;font-size:11px">false</span> (instead of `closed` there’s always the `halfOpen` alternative):</div><div> <span style="font-family:Menlo;font-size:11px;color:rgb(187,44,162)">init</span><span style="font-family:Menlo;font-size:11px">(_start: </span><span style="font-family:Menlo;font-size:11px;color:rgb(112,61,170)">Element</span><span style="font-family:Menlo;font-size:11px">, end: </span><span style="font-family:Menlo;font-size:11px;color:rgb(112,61,170)">Element</span><span style="font-family:Menlo;font-size:11px">, closed: </span><span style="font-family:Menlo;font-size:11px;color:rgb(112,61,170)">Bool</span><span style="font-family:Menlo;font-size:11px"> = </span><span style="font-family:Menlo;font-size:11px;color:rgb(187,44,162)">false</span><span style="font-family:Menlo;font-size:11px">)</span></div><div>The parameter is an optional parameter to minimize the impact on existing code that is currently initializing <span style="color:rgb(112,61,170);font-family:Menlo;font-size:11px">Ranges</span> using the <span style="color:rgb(187,44,162);font-family:Menlo;font-size:11px">init</span> method directly.</div><div>This parameter would also be a public property.</div><div><br></div><div>Then, the <span style="font-family:Menlo;font-size:11px">...</span> constructor would become:</div><div><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span style="color:rgb(187,44,162)">public</span><span> </span><span style="color:rgb(187,44,162)">func</span><span> ... <Pos : </span><span style="color:rgb(112,61,170)">ForwardIndex</span><span>> (minimum: </span><span style="color:rgb(79,129,135)">Pos</span><span>, maximum: </span><span style="color:rgb(79,129,135)">Pos</span><span>) -> </span><span style="color:rgb(112,61,170)">Range</span><span><</span><span style="color:rgb(79,129,135)">Pos</span><span>> {</span></p>
<p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span style="background-color:rgb(234,153,153)"> <font color="#000000">- return Range(_start: minimum, end: maximum.successor()) </font></span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><font color="#000000" style="background-color:rgb(0,255,0)"> + return<span> </span>Range<span>(_start: minimum, end: maximum, closed: </span>true<span>) </span></font></p>
<p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span>}</span></p></div><div><br></div><div>Also, the <span style="color:rgb(0,0,0);font-family:Menlo;font-size:11px">next()</span> method from the <span style="color:rgb(112,61,170);font-family:Menlo;font-size:11px">RangeGenerator</span> would become:</div><div><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(187,44,162)"><span>public</span><span style="color:rgb(0,0,0)"> </span><span>mutating</span><span style="color:rgb(0,0,0)"> </span><span>func</span><span style="color:rgb(0,0,0)"> next() -> </span><span style="color:rgb(79,129,135)">Element</span><span style="color:rgb(0,0,0)">? {</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><font color="#000000" style="background-color:rgb(234,153,153)"> - if startIndex == endIndex.successor() { return nil } </font><span style="color:rgb(0,0,0)"><br></span></p>
<p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span style="background-color:rgb(0,255,0)"><font color="#000000"> + if startIndex == (isClosed ? endIndex.successor() : endIndex) { return nil } </font></span></p>
<p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span style="color:rgb(187,44,162)"> </span><span style="color:rgb(187,44,162)"> </span><span style="color:rgb(187,44,162)">let</span><span> element = </span><span style="color:rgb(112,61,170)">startIndex</span></p>
<p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(61,29,129)"><span style="color:rgb(187,44,162)"> </span><span style="color:rgb(187,44,162)"> </span><span style="color:rgb(112,61,170)">startIndex</span><span style="color:rgb(0,0,0)">.</span><span>_successorInPlace</span><span style="color:rgb(0,0,0)">()</span></p>
<p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span style="color:rgb(187,44,162)"> </span><span style="color:rgb(187,44,162)"> </span><span style="color:rgb(187,44,162)">return</span><span> element</span></p>
<p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span>}</span></p></div><div><br></div><div>Performing this change has 2 main benefits:</div><div>1 — The <span style="color:rgb(112,61,170);font-family:Menlo;font-size:11px">Range</span> description and debugDescription properties would finally return a <b>true</b> self String representation. So, for instance, the description property would become:</div><div><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span style="color:rgb(187,44,162)">public</span><span> </span><span style="color:rgb(187,44,162)">var</span><span> description: </span><span style="color:rgb(112,61,170)">String</span><span> {</span></p>
<p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><font color="#000000" style="background-color:rgb(234,153,153)"><span style="white-space:pre-wrap"> </span>- return "\(startIndex)..<\(endIndex)" </font></p>
<p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><font color="#000000" style="background-color:rgb(0,255,0)"><span style="white-space:pre-wrap"> </span>+ return "\(startIndex)..\(isClosed ? "." : "<")\(endIndex)" </font></p>
<p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span>}</span></p></div><div><br></div><div>2 — It becomes a lot more intuitive. WYSIWYG. Also, by having the `closed` parameter in the init method, developers that create Ranges directly from the init method will actually know what type of Range they’re getting straight away: </div><div><span style="white-space:pre-wrap">        </span>- currently: <span style="font-family:Menlo;font-size:11px;color:rgb(112,61,170)">Range</span><span style="font-family:Menlo;font-size:11px">(start: </span><span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">1</span><span style="font-family:Menlo;font-size:11px">, end: </span><span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">10</span><span style="font-family:Menlo;font-size:11px">)</span> ==> is it <span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">1</span><span style="font-family:Menlo;font-size:11px">...</span><span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">10</span> or <span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">1</span><span style="font-family:Menlo;font-size:11px">..<</span><span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">10</span>?<br></div><div><span style="white-space:pre-wrap">        </span>- proposed: <span style="font-family:Menlo;font-size:11px;color:rgb(112,61,170)">Range</span><span style="font-family:Menlo;font-size:11px">(start: </span><span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">1</span><span style="font-family:Menlo;font-size:11px">, end: </span><span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">10</span><span style="font-family:Menlo;font-size:11px">, closed: </span><span style="font-family:Menlo;font-size:11px;color:rgb(187,44,162)">true</span><span style="font-family:Menlo;font-size:11px">)</span> ==> <span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">1</span><span style="font-family:Menlo;font-size:11px">...</span><span style="font-family:Menlo;font-size:11px;color:rgb(39,42,216)">10</span><br></div><div><br></div><div><br></div><div>This behavior is also present in Swift Collections:</div><div><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span style="color:rgb(187,44,162)">let</span><span> array = [</span><span style="color:rgb(39,42,216)">5</span><span>, </span><span style="color:rgb(39,42,216)">10</span><span>, </span><span style="color:rgb(39,42,216)">15</span><span>]</span></p>
<p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(79,129,135)"><span>array</span><span style="color:rgb(0,0,0)">.</span><span style="color:rgb(112,61,170)">count</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(0,132,0)">// 3</span></p>
<p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,132,0)"><span style="color:rgb(79,129,135)">array</span><span style="color:rgb(0,0,0)">.</span><span style="color:rgb(112,61,170)">endIndex</span><span style="color:rgb(0,0,0)"> </span><span>// 3 (should be 2)</span></p>
<p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,132,0)"><span style="color:rgb(79,129,135)">array</span><span style="color:rgb(0,0,0)">.</span><span style="color:rgb(112,61,170)">last</span><span style="color:rgb(0,0,0)">! </span><span>// 15 (apologies for the force unwrap </span><span style="line-height:normal;font-family:'Apple Color Emoji'">🤓</span><span>)</span></p></div><div><br></div><div>Again, the <span style="color:rgb(112,61,170);font-family:Menlo;font-size:11px">endIndex</span> returns an index that is outside the array bounds, which, in fact, acts as the array <span style="color:rgb(112,61,170);font-family:Menlo;font-size:11px">count</span>. Instead, it should have returned the index of the <span style="color:rgb(112,61,170);font-family:Menlo;font-size:11px">last</span> element.<br></div><div>I know that, in the comments, it’s explicit: “<span style="color:rgb(0,132,0);font-family:Menlo;font-size:11px">A 'past-the-end' element index; the successor of the last valid </span><span style="color:rgb(0,132,0);font-family:Menlo;font-size:11px">subscript argument.</span>”, but, in the end, it all comes down to readability.</div><div><br></div><div><br></div><div>What are your thoughts on this? Let me know.</div><div><br></div><div>Cheers,</div><div><br></div>-- <br><div><div dir="ltr"><div><div dir="ltr">Pedro Vieira<div><a href="http://pedrovieira.me" target="_blank">http://pedrovieira.me</a></div></div></div></div></div>
</div>