[swift-evolution] [Pitch] Retiring `where` from for-in loops

Xiaodi Wu xiaodi.wu at gmail.com
Sun Jun 12 19:22:21 CDT 2016


On Sun, Jun 12, 2016 at 9:23 AM, L. Mihalkovic <laurent.mihalkovic at gmail.com
> wrote:

>
> On Jun 12, 2016, at 3:21 PM, Thorsten Seitz <tseitz42 at icloud.com> wrote:
>
>
> Am 12.06.2016 um 12:02 schrieb L Mihalkovic <laurent.mihalkovic at gmail.com
> >:
>
>
> On Jun 11, 2016, at 11:45 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>
>
>
> On Sat, Jun 11, 2016 at 3:37 PM, Thorsten Seitz <tseitz42 at icloud.com>
> wrote:
>
>>
>>
>> Am 11.06.2016 um 22:29 schrieb L. Mihalkovic <
>> laurent.mihalkovic at gmail.com>:
>>
>>
>>
>> On Jun 11, 2016, at 9:53 PM, Thorsten Seitz via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>>
>>
>> Am 10.06.2016 um 18:28 schrieb Xiaodi Wu via swift-evolution <
>> swift-evolution at swift.org>:
>>
>> On Fri, Jun 10, 2016 at 6:10 AM, Karl <razielim at gmail.com> wrote:
>>
>>> -1
>>>
>>> * Swift is explicitly a C-family language. In most or all other C-family
>>> languages, for loop statements allow specification of conditions for
>>> exiting the loop but not for filtering. Therefore, Swift's use of `where`
>>> is unprecedented and needs to be learned anew by every user of Swift.
>>>
>>>
>>> When was this decided? I distinctly remember some bloke under Craig
>>> Federighi’s hair saying that it was time to “move beyond” C and essentially
>>> ditch legacy conventions which no longer make sense.
>>>
>>
>> I think you misunderstood my argument here. I don't mean that we should
>> yoke ourselves to C conventions, and we should absolutely ditch C
>> convention when it doesn't make sense. The big-picture argument here is
>> that `where` doesn't pass the bar of correcting a C convention that no
>> longer makes sense.
>>
>> FWIW, on the topic of syntax choices, here is what Chris Lattner had to
>> say on this list:
>>
>> Kevin got it exa*c*tly right, but I’d expand that last bit a bit to:
>>> “… pi*c*king the one that is most familiar to programmers in the
>>> extended *C* *family* is a good idea.["]
>>> The extended *C* *family* of language (whi*c*h in*c*ludes *C*, *C*++,
>>> Obj*C*, but also *C*#, Java, Javas*c*ript, and more) is
>>> an extremely popular and widely used set of languages that have a lot of
>>> surfa*c*e-level similarity. I
>>> don’t *c*laim to know the design rationale of all of these languages,
>>> but I surmise that this is not an
>>> a*c**c*ident: programmers move around and work in different languages,
>>> and this allows a non-expert in the
>>> language to understand what is going on. While there are things about
>>> *C* that are really unfortunate IMO
>>> (e.g. the de*c*larator/de*c*laration spe*c*ifier part of the grammar)
>>> there is a lot of goodness in the basi
>>> *c*operator set, fo*c*us on dot syntax, and more.
>>> I do agree that there are some benefits to dit*c*hing bra*c*es and
>>> relying on indentation instead, but there are
>>> also downsides. Deviating from the *C* *family* in this respe*c*t would
>>> have to provide **overwhelmingly** large
>>> advantages for us to take su*c*h a plunge, and they simply don’t exist.
>>
>>
>>
>>> As I understand it, Swift is a new language with new conventions. It is
>>> desirable to align as many of those as possible with existing conventions
>>> so as to be easily learned, but if you limit Swift to other languages
>>> conventions you deny it any identity. Did Python ask anybody’s opinion
>>> before dropping curly-braces? Did people learn whatever Perl is supposed to
>>> be? Look at C’s hieroglyphic for loops!
>>>
>>
>> I don't think we disagree here.
>>
>>
>>>
>>> Realistically, “for … in … while” is not going to cause incredible
>>> confusion. Removing it would cause a lot of frustration. You can’t on the
>>> one hand say our users are comfortable with the axioms of C’s hieroglyphic
>>> loops, and on the other hand say “for x in y while" is confusing.
>>>
>>> Again, as I said, once you've mastered something, by definition you find
>>> it not confusing. Why should we doom x% of new users to writing a loop
>>> incorrectly at least once when we don't have to?
>>>
>>>
>>> Ah, but if you’re not “doomed” to failing once, how will you ever master
>>> anything? Nobody knew how to write a C for-loop until someone showed them
>>> (and even then…). Nobody is going to just open a REPL and start writing
>>> code, with zero prior understanding of what Swift syntax looks like.
>>>
>>
>> The thought here is along the lines of what Chris said, quoted above, and
>> repeated here: "The extended C family of language [...] is an extremely
>> popular and widely used set[;] programmers move around and work in
>> different languages, and [aligning to expectations arising from other C
>> family languages] allows a non-expert in the language to understand what is
>> going on." By contrast, the `where` clause violates that expectation and I
>> do not see "overwhelmingly large advantages" for doing so.
>>
>>
>> What about C#'s `where` then? As C# is a member of the C family languages
>> `where` is not violating expectations!
>>
>>
>> Where is not exactly a part of c# it belongs to linq
>>
>>
>> And that is not a part of C#??
>>
>
> SQL is a domain-specific language, and LINQ is an internal domain-specific
> language with a language extension for C#. Neither is a general purpose
> language.
>
> Your example actually goes to one of Laurent's points. Should the Swift
> core team or an enterprising community member propose a set of similarly
> powerful tools, along with a set of language extensions that add syntactic
> sugar for them, I (and I think Laurent, if I understand him correctly)
> would absolutely be in favor of such an addition. But as it is, `where` is
> an odd duckling. Just as you say, it looks like a component of a query
> language, but it does no such thing. In a for loop, it does some filtering,
> but until recently it functioned like a comma in `while` loops. Look at
> those other keywords which make this sugar possible in C#: in your example,
> `from` and `select`. We don't have any of that intrastructure in Swift.
>
>
>
> IMHO the team has taken an Ockham Axe to the grammar: in the presence of
> multiple ways to produce the same or an acceptable stand-in (for eg when
> the only difference is an acceptable temporary perf setback), then the
> solution requiring the least assumptions on the compiler wins.
>
> I would even extend this rule with the corollary that between an
> assumption materialized as a type checker rule and an assumption
> materialized as a full blown extra language keyword, there might be a bias
> to accept the former if it kills the latter. But this is just my personal
> inference of what their decision heuristic might be based solely on what I
> saw. My sole interest in trying to understand their decision making process
> is to try to avoid proposals that have little to no chance to go anywhere,
> as well as trying to present ones that will align better with where the
> language is going.
>
> In this instance, WHERE is a heavy assumption on the compiler for no
> greater gain than filters can provide. So I think we save the WHERE keyword
> for an outcome that will be really worth it! Something along the idea of
> Linq, but with a proper Swift feel to it. What does it look like? I cannot
> say yet. But the good news is that having taken WHERE out now will make
> that next step a purely additive process (nothing will be taken out then,
> but a big thing will be gained).
>
>
> I am all for extending `for` to be more like Scala’s `for`-expression or
> Haskell’s `do`-notation. LINQ might be too focused on querying.
> Extending the current `where` to be syntax sugar for `filter` would be
> quite straightforward, though, so I’m not sure whether removing it first is
> really necessary.
> That was not object of the pitch to retire `where` from `for`-loops,
> though. Instead the argument was (and still is) that `where` might be
> misunderstood there by beginners and should therefore be removed. This
> argument would apply to an extended `where` unchanged, because I can still
> write the exact same code as being criticized now and can even do more
> complicated things which would by even more hard to understand for
> beginners.
> That’s the argument I disagree with.
>
>
>
>
> The following is an example from MSDN with `where` clearly beaing a
>> keyword:
>>
>> *var* numQuery = *from* num *in* numbers *where* (num % 2) == 0 *select* num;
>>
>>
>
> Here is food for your thoughts, you think WHERE is a keyword?! then look
> at this:
>
> var numbers = new int[]{0,1,23,4,5,6,7,87,9};
> var numQuery = from num in numbers where (num % 2) == 0 select num;
>
> Program does not compile:
>
> // Error CS1935: An implementation of `Where' query expression pattern
> could not be found. Are you missing `System.Linq' using directive or
> `System.Core.dll' assembly reference? (CS1935) (SessionsFinder)
>
> using System.Linq;
> var numbers = new int[]{0,1,23,4,5,6,7,87,9};
> var numQuery = from num in numbers where (num % 2) == 0 select num;
>
> That program does compiles and runs fine. This tells you that “where" is
> not at all the ordinary keyword that it appears to be. hence my “it is not
> C# per-se, it is Linq”.
>
>
> Digging into https://msdn.microsoft.com/en-us/library/bb397909.aspx and
> https://msdn.microsoft.com/en-us/library/the35c6y.aspx tells me the
> following:
> (1) `from`, `where` etc. are contextual keywords, but they are nonetheless
> keywords of C# and expressly *not* a specific feature of LINQ: "Although
> these new features are all used to a degree with LINQ queries, they are not
> limited to LINQ and can be used in any context where you find them useful."
> (2) the compiler translates them into standard method calls (like Scala’s
> `for`-expression or Haskell’s `do`-notation). That is an integral part of
> C#.
> (3) your error message originates from not having an implementation of a
> `Where`-method in scope. You could have provided one yourself instead of
> `using System.Linq`.
>
>
> Of course the compiler knows about it... My exact wording was "ordinary
> keyword" implying, "it is one, just not in the sense you are giving it
> right now". Knowing you would want to dig further I had to find a
> description that was true albeit leaving the fine details out (i doubt too
> may actually care about the distinction between contextual and
> non-contextual kwd, or the fact that sql is turing complete ;) ).
>
> My thought is to accept the downgrading from its current status because
> its complete behavior is limited and hetched in blood inside the grammar,
> therefore not extensible without grammar alterations; thereby leaving the
> door open for a future re-introduction ala-Linq in v4+. I use the term
> "ala-linq" as a placeholder for "some sort of more dynamic behavior
> resulting from a close collaboration between compiler, stdlib, runtime and
> user code". And i still believe nobody outside doug, joe, chris, or jordan
> cares about the actual implementation details.
>
> Again, this is just a personal view on what looks IMHO like a great
> opportunity.
>

Well said. Like you, I don't know what the core team's feelings would be
with respect to contextual sugar for domain-specific uses. IMHO, it is a
very neat concept. If it does arrive in Swift 4+, then `where` as sugar for
`filter()` should come with that. If that's not the direction that the core
team wants to go, I fail to see why filtering a sequence specifically as
part of a loop should be privileged above any other useful operation that
would have a similarly strong claim to sugar.


> And if you still wonder, then rewrite the code so that “WHERE” no longer
> looks like a keyword:
>
> var numQuery2 = numbers.Where( num => num %2 == 0); // exact same result
> as numQuery1
>
>
> That’s exactly what I expected as it is the standard way syntactic
> language sugar is mapped to customizable behavior. Haskell’s `do`-notation
> or Scala’s `for`-expression are mapped likewise to functions on the types
> used.
>
> -Thorsten
>
>
>
>
> These are other real life examples from a OS X tool I wrote recently:
>
> IEnumerable<Task<Session>> asyncOps = from session in sessions.Values
> select parseSessionDetails(updater, parser, session);
> var ul = node.Descendants().Where(x => x.Name == "ul").First();
>
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160612/00385b87/attachment.html>


More information about the swift-evolution mailing list