
List Comprehension in Swift - DaNmarner
https://duan.ca/2017/12/09/list-comprehension-in-swift/
======
59nadir
I really dislike the proposed way to do this. If this is "Swifty", give me
something else.

The Haskell list comprehension made sense to me the first time I saw it, but
there is no way I'd know what the mess in the article was doing until someone
explained to me.

Design has to come first. You can't just go with what's "Swifty" if that
doesn't convey what's happening in a reasonable fashion.

~~~
coldtea
> _The Haskell list comprehension made sense to me the first time I saw it,
> but there is no way I 'd know what the mess in the article was doing until
> someone explained to me._

Given that this is basic Swift, the complaint makes no sense:

    
    
      let a = Array(1..<5, 3..<5, where: { n, _ in n % 2 == 0 }) 
      { ($0, $1) }
      // [(2,3),(2,4),(2,5) ...
    
      let a = Array(1..<5, 3..<5) { ($0, $1) }
      // [(1,3),(1,4),(1,5),(2,3),(2,4) ...

~~~
dozzie
What the heck? Why does it end up like this? I see very little connection, and
I've seen list comprehensions in several different languages.

~~~
rbehrends
A list comprehension is basically a cartesian product with a filter and map
applied to it.

What we have here is an array constructor, which takes one or more ranges as
its arguments. The named `where:` argument provides the filter closure and the
final argument is the map, using standard trailing closure syntax.

In fact, other than the missing implementation of the constructor, this would
be perfectly legal Swift code today.

I'm not sure what's there to be confused about. `x..<y` describes an open
range (including x, excluding y). `{x, y in ... }` is normal Swift closure
syntax, where `x` and `y` are the arguments; you can also use positional
arguments as in `{ ($0, $1) }` instead.

If the final argument of a function is a closure, it can be (as in Ruby) be
written after the closing parenthesis of the function call.

~~~
dozzie
> A list comprehension is basically a cartesian product with a filter and map
> applied to it.

Ah yes, explain me more the concept I've been using for a dozen years, surely
I don't understand it. And with mix of terminology from as far topics as set
theory and functional programming, to lessen the confusion.

> What we have here is an array constructor, which takes one or more ranges as
> its arguments.

I would never have guessed the arguments thing.

> `x..<y` describes an open range (including x, excluding y).

I would never have guessed it's open range.

> you can also use positional arguments as in `{ ($0, $1) }` instead.

I would never have guessed, and I was writing in Perl with its magic variables
for more than a half of my professional career and most of my studies.

And that's the whole point: the syntax is so atrocious that it takes training
to understand it. I can accept the _semantics_ to need training to understand
(as I did with Perl, Erlang, or now doing with Ada), but with this we haven't
hit that level yet.

~~~
rbehrends
So, your problem is that Swift's syntax in general isn't to your tastes, not
the list comprehension proposal as such? Fair enough, but that's not what you
were saying.

~~~
dozzie
> Fair enough, but that's not what you were saying.

Then tell me, what was I saying?

>> I see very little connection, and I've seen list comprehensions in several
different languages.

And then...

> So, your problem is [...] not the list comprehension proposal as such?

...you should get off your high horse. Your juggling with math terminology
earlier is not impressive. I know you wanted to sound _smart_ , but you
overdid it and got _smartass_ instead.

~~~
rbehrends
> Then tell me, what was I saying?

Let me rephrase then: what you seemed to be saying.

> ...you should get off your high horse. Your juggling with math terminology
> earlier is not impressive.

I'm not sure where you got that impression. I learned about Cartesian products
in high school. Everything else I was talking about is at most first year
computer science stuff.

I was simply trying to be precise, using the most basic terminology I could
think of.

------
Someone
_List_ comprehension is a bad idea, IMO. They were an improvement over not
having anything like it, but python has improved over it
([https://wiki.python.org/moin/Generators](https://wiki.python.org/moin/Generators))

⇒ _Generator expressions_ are the way to go. They would give you the sequence
of elements without generating the (potentially enormous) data structure.

From there, methods to reify the items in a sequence would give you your list,
array, or dictionary, where needed.

So, for Swift, I wouldn’t use

    
    
        Array(1..<5, 3..<5) { ($0, $1) }
    

but the slightly longer

    
    
        Array(for i in 1..<5, j in 3..<5 yield (i,j))
    

I’m not sure that is easy to fit in the existing parser, though. If it can be
fit in, I would allow that code in ‘normal’ nested for loops, too.

~~~
rbehrends
This does not require a different syntax. You'd just construct a lazy list
instead of an array.

Neither is an improvement over the other. Sometimes it's more important to
have the results up front fast, sometimes generating them on demand is better.

------
thewayfarer
> (Can’t wait until we can have variadic generic parameters!)

This.

~~~
stevedonovan
It's a genuinely hard problem. There are proposals for Rust, but nothing
actionable. Catching up with all the other cool things takes precedence.
(side-note: Rust dodged the problem by using macros. As a C++ guy, I was a
little uneasy with this, but they're _mostly_ hygenic)

~~~
saghm
Out of curiosity, what are the non-hygienic parts are of Rust macros? I'm
pretty sure that non-procedural macros are hygienic, although I don't know a
whole lot about the implementation of procedural macros.

~~~
steveklabnik
Quoting the subreddit the other day:

> Nothing except local variable names (and nested macro capture names) is
> hygienic in macro_rules in fact.

It's a thing that's being fixed in the new macro systems, yeah.

------
ralfd
Can someone explain the n modulo operation to me?

~~~
fiala__
it's an easy way to filter out odd/even numbers. `n % m` means "divide `n` by
`m` and return the remainder". If you divide any even number by 2, the
remainder is always 0, if you divide an odd number, it's always 1. Translate
that to a boolean and you've got a nice odd/even filter.

