Extending the for loop to have multiple clauses


(Chris Eidhof) #1

I think it could be really nice to extend the for-loop so that it can have multiple clauses. Much like in the if-let with multiple clauses, I could imagine a for-loop with multiple clauses:

var cards: [(Suit,Rank)] = []
for x in suits, y in ranks {
  cards.append((x,y))
}

This would be the same as writing:

var cards: [(Suit,Rank)] = []
for x in suits {
  for y in ranks {
    cards.append((x,y))}
  }
}

You could also do something like:

for x in input1, y in (x..<end) {
   // Do something with (x,y)
}

In fact, once we would have that, we could combine both if-let and for, and make it more general, to end up with something like Haskell’s do-notation or C#’s LINQ. But that might be taking it too far...

Chris


(Chéyo Jiménez) #2

The proposed extension looks to me like is iterating both the suits and
rank at the same time. In that case I think is a great idea.

var cards: [(Suit,Rank)] = []
for x in suits, y in ranks {
  cards.append((x,y))
}

What I am expecting:

x1, y1
x2, y2
x3, y3
etc

This reminds me that this is one of the ways I use the C-style for-loop.
The alternative now:

for (index, x) in suits.enumerate(){
   let y = ranks[index]
   cards.append((x,y))

}

···

On Wed, Dec 9, 2015 at 12:00 PM, Chris Eidhof via swift-evolution < swift-evolution@swift.org> wrote:

I think it could be really nice to extend the for-loop so that it can have
multiple clauses. Much like in the if-let with multiple clauses, I could
imagine a for-loop with multiple clauses:

var cards: [(Suit,Rank)] = []
for x in suits, y in ranks {
  cards.append((x,y))
}

This would be the same as writing:

var cards: [(Suit,Rank)] = []
for x in suits {
  for y in ranks {
    cards.append((x,y))}
  }
}

You could also do something like:

for x in input1, y in (x..<end) {
   // Do something with (x,y)
}

In fact, once we would have that, we could combine both if-let and for,
and make it more general, to end up with something like Haskell’s
do-notation or C#’s LINQ. But that might be taking it too far...

Chris
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Douglas Gregor) #3

Isn’t this just

  for (x, y) in zip2(suits, ranks) {
  }

?

  - Doug

···

On Dec 9, 2015, at 12:00 PM, Chris Eidhof via swift-evolution <swift-evolution@swift.org> wrote:

I think it could be really nice to extend the for-loop so that it can have multiple clauses. Much like in the if-let with multiple clauses, I could imagine a for-loop with multiple clauses:

var cards: [(Suit,Rank)] = []
for x in suits, y in ranks {
cards.append((x,y))
}


(Brent Royal-Gordon) #4

I think it could be really nice to extend the for-loop so that it can have multiple clauses. Much like in the if-let with multiple clauses, I could imagine a for-loop with multiple clauses:

var cards: [(Suit,Rank)] = []
for x in suits, y in ranks {
cards.append((x,y))
}

Isn’t this just

  for (x, y) in zip2(suits, ranks) {
  }

?

No, apparently it’s supposed to be all permutations of both types. But I too read it as syntactic sugar for a zip, which I think is a great reason not to add this syntax.

···

--
Brent Royal-Gordon
Architechies


(Douglas Gregor) #5

Ah. In that case, I’d much rather have a library function that indicates that we’re getting all permutations. This doesn’t feel like it belongs in the language at all, but in the library.

  - Doug

···

On Dec 9, 2015, at 1:43 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

I think it could be really nice to extend the for-loop so that it can have multiple clauses. Much like in the if-let with multiple clauses, I could imagine a for-loop with multiple clauses:

var cards: [(Suit,Rank)] = []
for x in suits, y in ranks {
cards.append((x,y))
}

Isn’t this just

  for (x, y) in zip2(suits, ranks) {
  }

?

No, apparently it’s supposed to be all permutations of both types. But I too read it as syntactic sugar for a zip, which I think is a great reason not to add this syntax.


(Maxwell Swadling) #6

We do have this library function, it is flatMap.

The desired feature here seems to be desugaring for loops

for x in xs, y in ys, z in zs {
  ...
}

into:

xs.flatMap { x in ys.flatMap { y in zs.map { z in ... } } }

So as above you would get:

suits.flatMap { x in ranks.map { y in cards.append((x,y)) } }

···

On 9 Dec 2015, at 1:44 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 9, 2015, at 1:43 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

I think it could be really nice to extend the for-loop so that it can have multiple clauses. Much like in the if-let with multiple clauses, I could imagine a for-loop with multiple clauses:

var cards: [(Suit,Rank)] = []
for x in suits, y in ranks {
cards.append((x,y))
}

Isn’t this just

  for (x, y) in zip2(suits, ranks) {
  }

?

No, apparently it’s supposed to be all permutations of both types. But I too read it as syntactic sugar for a zip, which I think is a great reason not to add this syntax.

Ah. In that case, I’d much rather have a library function that indicates that we’re getting all permutations. This doesn’t feel like it belongs in the language at all, but in the library.

  - Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Frederick Kellison-Linn) #7

Agreed. My original response, which did not get reply all’ed:

To me, this notation seems slightly confusing. The current syntax with an inner loop makes the time complexity apparent, and more accurately represents what you are trying to do (in this example, build the Cartesian product of suits and ranks).

The "for x in suits, y in ranks" notation hides the inner loop(s) and is not immediately apparent in its function (does this construct increment both x and y every iteration?). I definitely have seen the type of construct you are proposing many times, so there is value in considering a more concise way of writing it, but perhaps there is better notation.

FKL

···

On Dec 9, 2015, at 4:31 PM, J. Cheyo Jimenez via swift-evolution <swift-evolution@swift.org> wrote:

The proposed extension looks to me like is iterating both the suits and rank at the same time. In that case I think is a great idea.

var cards: [(Suit,Rank)] = []
for x in suits, y in ranks {
  cards.append((x,y))
}

What I am expecting:

x1, y1
x2, y2
x3, y3
etc

This reminds me that this is one of the ways I use the C-style for-loop.
The alternative now:

for (index, x) in suits.enumerate(){
   let y = ranks[index]
   cards.append((x,y))
}

On Wed, Dec 9, 2015 at 12:00 PM, Chris Eidhof via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
I think it could be really nice to extend the for-loop so that it can have multiple clauses. Much like in the if-let with multiple clauses, I could imagine a for-loop with multiple clauses:

var cards: [(Suit,Rank)] = []
for x in suits, y in ranks {
  cards.append((x,y))
}

This would be the same as writing:

var cards: [(Suit,Rank)] = []
for x in suits {
  for y in ranks {
    cards.append((x,y))}
  }
}

You could also do something like:

for x in input1, y in (x..<end) {
   // Do something with (x,y)
}

In fact, once we would have that, we could combine both if-let and for, and make it more general, to end up with something like Haskell’s do-notation or C#’s LINQ. But that might be taking it too far...

Chris
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Chris Eidhof) #8

Exactly! Just like multiple-if-let is the same as flatMap on optionals, this is a better syntax for flatMap on arrays.

To make it more clear that it’s a nested loop, we could also consider:

for x in xs, for y in ys {
}

But I’m not sure if it actually is clearer. (You could totally still interpret this as a zip, rather than a flatMap).

Chris

···

On 09 Dec 2015, at 16:51, Maxwell Swadling <maxs@apple.com> wrote:

On 9 Dec 2015, at 1:44 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Dec 9, 2015, at 1:43 PM, Brent Royal-Gordon <brent@architechies.com <mailto:brent@architechies.com>> wrote:

I think it could be really nice to extend the for-loop so that it can have multiple clauses. Much like in the if-let with multiple clauses, I could imagine a for-loop with multiple clauses:

var cards: [(Suit,Rank)] = []
for x in suits, y in ranks {
cards.append((x,y))
}

Isn’t this just

  for (x, y) in zip2(suits, ranks) {
  }

?

No, apparently it’s supposed to be all permutations of both types. But I too read it as syntactic sugar for a zip, which I think is a great reason not to add this syntax.

Ah. In that case, I’d much rather have a library function that indicates that we’re getting all permutations. This doesn’t feel like it belongs in the language at all, but in the library.

  - Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

We do have this library function, it is flatMap.

The desired feature here seems to be desugaring for loops

for x in xs, y in ys, z in zs {
  ...
}

into:

xs.flatMap { x in ys.flatMap { y in zs.map { z in ... } } }

So as above you would get:

suits.flatMap { x in ranks.map { y in cards.append((x,y)) } }


(Krzysztof Siejkowski) #9

+1. Way more readable than nested loops and consistent with already existing syntax.

···

-----Original Message-----
From: Chris Eidhof via swift-evolution <swift-evolution@swift.org>
Reply: Chris Eidhof <chris@eidhof.nl>
Date: December 9, 2015 at 9:00:26 PM
To: swift-evolution@swift.org <swift-evolution@swift.org>
Subject: [swift-evolution] Extending the for loop to have multiple clauses

I think it could be really nice to extend the for-loop so that it can have multiple clauses.
Much like in the if-let with multiple clauses, I could imagine a for-loop with multiple
clauses:

var cards: [(Suit,Rank)] = []
for x in suits, y in ranks {
cards.append((x,y))
}

This would be the same as writing:

var cards: [(Suit,Rank)] = []
for x in suits {
for y in ranks {
cards.append((x,y))}
}
}

You could also do something like:

for x in input1, y in (x..> // Do something with (x,y)
}

In fact, once we would have that, we could combine both if-let and for, and make it more
general, to end up with something like Haskell’s do-notation or C#’s LINQ. But that might
be taking it too far...

Chris
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(ilya) #10

You can define a 'times' operation to work with any sequences, e.g.

import Foundation

enum Suits: String {
    case Spades = ":spades:"
    case Hearts = ":heart:"
    case Diamonds = ":diamonds:"
    case Clubs = ":clubs:"
}

let suits:[Suits] = [.Spades, .Hearts, .Diamonds, .Clubs]
let ranks = ["A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3",
"2"]

infix operator ⨉ {}

func ⨉<A:SequenceType, B:SequenceType>(lhs: A, rhs: B)
    -> [(A.Generator.Element, B.Generator.Element)] {

    return lhs
        .map{ left in rhs.map{ right in (left, right) }}
        .reduce([], combine: +)
}

[1, 2] ⨉ [3, 4]

func shuffled() -> [String] {
    var cards:[String] = []

    for (suit, rank) in suits ⨉ ranks {
        let random = Int(abs(rand())) % (cards.count + 1)
        cards.insert(suit.rawValue + rank, atIndex: random)
    }

    return cards
}

shuffled().joinWithSeparator(" ")

···

On Wed, Dec 9, 2015 at 23:00 Chris Eidhof via swift-evolution < swift-evolution@swift.org> wrote:

I think it could be really nice to extend the for-loop so that it can have
multiple clauses. Much like in the if-let with multiple clauses, I could
imagine a for-loop with multiple clauses:

var cards: [(Suit,Rank)] = []
for x in suits, y in ranks {
  cards.append((x,y))
}

This would be the same as writing:

var cards: [(Suit,Rank)] = []
for x in suits {
  for y in ranks {
    cards.append((x,y))}
  }
}

You could also do something like:

for x in input1, y in (x..<end) {
   // Do something with (x,y)
}

In fact, once we would have that, we could combine both if-let and for,
and make it more general, to end up with something like Haskell’s
do-notation or C#’s LINQ. But that might be taking it too far...

Chris
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(ilya) #11

The proposed extension looks to me like is iterating both the suits and

rank at the same time.
So, it looks like different things to different people, which doesn't bode
well for its acceptance.

The alternative now:

That's what zip is for:

var count = 0

for (x, y) in zip(ranks, suits) {
    count += 1
}

count // 4

···

On Thu, Dec 10, 2015 at 12:31 AM, J. Cheyo Jimenez via swift-evolution < swift-evolution@swift.org> wrote:

The proposed extension looks to me like is iterating both the suits and
rank at the same time. In that case I think is a great idea.

var cards: [(Suit,Rank)] = []
for x in suits, y in ranks {
  cards.append((x,y))
}

What I am expecting:

x1, y1
x2, y2
x3, y3
etc

This reminds me that this is one of the ways I use the C-style for-loop.
The alternative now:

for (index, x) in suits.enumerate(){
   let y = ranks[index]
   cards.append((x,y))

}

On Wed, Dec 9, 2015 at 12:00 PM, Chris Eidhof via swift-evolution < > swift-evolution@swift.org> wrote:

I think it could be really nice to extend the for-loop so that it can
have multiple clauses. Much like in the if-let with multiple clauses, I
could imagine a for-loop with multiple clauses:

var cards: [(Suit,Rank)] = []
for x in suits, y in ranks {
  cards.append((x,y))
}

This would be the same as writing:

var cards: [(Suit,Rank)] = []
for x in suits {
  for y in ranks {
    cards.append((x,y))}
  }
}

You could also do something like:

for x in input1, y in (x..<end) {
   // Do something with (x,y)
}

In fact, once we would have that, we could combine both if-let and for,
and make it more general, to end up with something like Haskell’s
do-notation or C#’s LINQ. But that might be taking it too far...

Chris
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Douglas Gregor) #12

I suspect I’ll always have a very hard time reading a comma-separated list of clauses as producing a nested iteration.

  - Doug

···

On Dec 9, 2015, at 2:11 PM, Chris Eidhof <chris@eidhof.nl> wrote:

Exactly! Just like multiple-if-let is the same as flatMap on optionals, this is a better syntax for flatMap on arrays.

To make it more clear that it’s a nested loop, we could also consider:

for x in xs, for y in ys {
}

But I’m not sure if it actually is clearer. (You could totally still interpret this as a zip, rather than a flatMap).


(David Waite) #13

You could have a function (lets call it cross for lack of me knowing a better mathematical term) that takes in multiple CollectionTypes and returns a CollectionType of all the combinations of the constituent sequence type elements.

for (x,y) in cross(0..<width, 0..<height) { … }

My swift-fu however is not yet strong enough for me to know how to avoid the backing generators being CrossGenerator2, CrossGenerator3, etc.

-DW

···

On Dec 9, 2015, at 3:11 PM, Chris Eidhof via swift-evolution <swift-evolution@swift.org> wrote:

Exactly! Just like multiple-if-let is the same as flatMap on optionals, this is a better syntax for flatMap on arrays.

To make it more clear that it’s a nested loop, we could also consider:

for x in xs, for y in ys {
}

But I’m not sure if it actually is clearer. (You could totally still interpret this as a zip, rather than a flatMap).

Chris

On 09 Dec 2015, at 16:51, Maxwell Swadling <maxs@apple.com <mailto:maxs@apple.com>> wrote:

On 9 Dec 2015, at 1:44 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Dec 9, 2015, at 1:43 PM, Brent Royal-Gordon <brent@architechies.com <mailto:brent@architechies.com>> wrote:

I think it could be really nice to extend the for-loop so that it can have multiple clauses. Much like in the if-let with multiple clauses, I could imagine a for-loop with multiple clauses:

var cards: [(Suit,Rank)] = []
for x in suits, y in ranks {
cards.append((x,y))
}

Isn’t this just

  for (x, y) in zip2(suits, ranks) {
  }

?

No, apparently it’s supposed to be all permutations of both types. But I too read it as syntactic sugar for a zip, which I think is a great reason not to add this syntax.

Ah. In that case, I’d much rather have a library function that indicates that we’re getting all permutations. This doesn’t feel like it belongs in the language at all, but in the library.

  - Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

We do have this library function, it is flatMap.

The desired feature here seems to be desugaring for loops

for x in xs, y in ys, z in zs {
  ...
}

into:

xs.flatMap { x in ys.flatMap { y in zs.map { z in ... } } }

So as above you would get:

suits.flatMap { x in ranks.map { y in cards.append((x,y)) } }

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Chris Eidhof) #14

Yes, it’d be great if you can combine it with where. Alternatively, it could be interesting to instead explore array comprehension syntax, rather than making `for` smarter. That’d allow for a more functional style. For example, Norvig’s spelling corrector (http://norvig.com/spell-correct.html) would then be very straightforward to port.

Re the x operator: it’s even easier if you define it with flatMap. However, the for syntax is different, because the second clause can depend on the first...

Chris

···

On 09 Dec 2015, at 15:48, Jacob Bandes-Storch <jtbandes@gmail.com> wrote:

Are you expecting that this could be combined with "where" clauses?

for x in range1 where x%2==0, y in range2 where y%3==0 {}

for x in range1, y in range2 where x%2==0 && y%3==0 {}

I think it's a good idea, but the implications may be less than obvious.
On Wed, Dec 9, 2015 at 12:37 PM krzysztof@siejkowski.net <mailto:krzysztof@siejkowski.net> via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
+1. Way more readable than nested loops and consistent with already existing syntax.

-----Original Message-----
From: Chris Eidhof via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>
Reply: Chris Eidhof <chris@eidhof.nl <mailto:chris@eidhof.nl>>
Date: December 9, 2015 at 9:00:26 PM
To: swift-evolution@swift.org <mailto:swift-evolution@swift.org> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>
Subject: [swift-evolution] Extending the for loop to have multiple clauses

> I think it could be really nice to extend the for-loop so that it can have multiple clauses.
> Much like in the if-let with multiple clauses, I could imagine a for-loop with multiple
> clauses:
>
> var cards: [(Suit,Rank)] = []
> for x in suits, y in ranks {
> cards.append((x,y))
> }
>
> This would be the same as writing:
>
> var cards: [(Suit,Rank)] = []
> for x in suits {
> for y in ranks {
> cards.append((x,y))}
> }
> }
>
> You could also do something like:
>
> for x in input1, y in (x..> // Do something with (x,y)
> }
>
> In fact, once we would have that, we could combine both if-let and for, and make it more
> general, to end up with something like Haskell’s do-notation or C#’s LINQ. But that might
> be taking it too far...
>
> Chris
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Colin Barrett) #15

Indeed. In fact, ilya posted this already with an implementation upthread :slight_smile:

BTW, the mathematical term is “Cartesian product”, named so because this operation is how you construct the famous Cartesian xy-coordinate system we all learned in Algebra class.

FWIW I agree that this should be a library function and not in the language.

-Colin

···

On Dec 9, 2015, at 5:31 PM, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

You could have a function (lets call it cross for lack of me knowing a better mathematical term) that takes in multiple CollectionTypes and returns a CollectionType of all the combinations of the constituent sequence type elements.

for (x,y) in cross(0..<width, 0..<height) { … }

My swift-fu however is not yet strong enough for me to know how to avoid the backing generators being CrossGenerator2, CrossGenerator3, etc.

-DW

On Dec 9, 2015, at 3:11 PM, Chris Eidhof via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Exactly! Just like multiple-if-let is the same as flatMap on optionals, this is a better syntax for flatMap on arrays.

To make it more clear that it’s a nested loop, we could also consider:

for x in xs, for y in ys {
}

But I’m not sure if it actually is clearer. (You could totally still interpret this as a zip, rather than a flatMap).

Chris

On 09 Dec 2015, at 16:51, Maxwell Swadling <maxs@apple.com <mailto:maxs@apple.com>> wrote:

On 9 Dec 2015, at 1:44 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Dec 9, 2015, at 1:43 PM, Brent Royal-Gordon <brent@architechies.com <mailto:brent@architechies.com>> wrote:

I think it could be really nice to extend the for-loop so that it can have multiple clauses. Much like in the if-let with multiple clauses, I could imagine a for-loop with multiple clauses:

var cards: [(Suit,Rank)] = []
for x in suits, y in ranks {
cards.append((x,y))
}

Isn’t this just

  for (x, y) in zip2(suits, ranks) {
  }

?

No, apparently it’s supposed to be all permutations of both types. But I too read it as syntactic sugar for a zip, which I think is a great reason not to add this syntax.

Ah. In that case, I’d much rather have a library function that indicates that we’re getting all permutations. This doesn’t feel like it belongs in the language at all, but in the library.

  - Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

We do have this library function, it is flatMap.

The desired feature here seems to be desugaring for loops

for x in xs, y in ys, z in zs {
  ...
}

into:

xs.flatMap { x in ys.flatMap { y in zs.map { z in ... } } }

So as above you would get:

suits.flatMap { x in ranks.map { y in cards.append((x,y)) } }

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jacob Bandes-Storch) #16

Are you expecting that this could be combined with "where" clauses?

for x in range1 where x%2==0, y in range2 where y%3==0 {}

for x in range1, y in range2 where x%2==0 && y%3==0 {}

I think it's a good idea, but the implications may be less than obvious.

···

On Wed, Dec 9, 2015 at 12:37 PM krzysztof@siejkowski.net via swift-evolution <swift-evolution@swift.org> wrote:

+1. Way more readable than nested loops and consistent with already
existing syntax.

-----Original Message-----
From: Chris Eidhof via swift-evolution <swift-evolution@swift.org>
Reply: Chris Eidhof <chris@eidhof.nl>
Date: December 9, 2015 at 9:00:26 PM
To: swift-evolution@swift.org <swift-evolution@swift.org>
Subject: [swift-evolution] Extending the for loop to have multiple clauses

> I think it could be really nice to extend the for-loop so that it can
have multiple clauses.
> Much like in the if-let with multiple clauses, I could imagine a
for-loop with multiple
> clauses:
>
> var cards: [(Suit,Rank)] = []
> for x in suits, y in ranks {
> cards.append((x,y))
> }
>
> This would be the same as writing:
>
> var cards: [(Suit,Rank)] = []
> for x in suits {
> for y in ranks {
> cards.append((x,y))}
> }
> }
>
> You could also do something like:
>
> for x in input1, y in (x..> // Do something with (x,y)
> }
>
> In fact, once we would have that, we could combine both if-let and for,
and make it more
> general, to end up with something like Haskell’s do-notation or C#’s
LINQ. But that might
> be taking it too far...
>
> Chris
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(ilya) #17

the second clause can depend on the first...

For those cases, I'd really prefer plain dumb old for loop on two separate
visually distinct lines :slight_smile:

···

On Thu, Dec 10, 2015 at 12:13 AM, Chris Eidhof via swift-evolution < swift-evolution@swift.org> wrote:

Yes, it’d be great if you can combine it with where. Alternatively, it
could be interesting to instead explore array comprehension syntax, rather
than making `for` smarter. That’d allow for a more functional style. For
example, Norvig’s spelling corrector (http://norvig.com/spell-correct.html)
would then be very straightforward to port.

Re the x operator: it’s even easier if you define it with flatMap.
However, the for syntax is different, because the second clause can depend
on the first...

Chris

On 09 Dec 2015, at 15:48, Jacob Bandes-Storch <jtbandes@gmail.com> wrote:

Are you expecting that this could be combined with "where" clauses?

for x in range1 where x%2==0, y in range2 where y%3==0 {}

for x in range1, y in range2 where x%2==0 && y%3==0 {}

I think it's a good idea, but the implications may be less than obvious.
On Wed, Dec 9, 2015 at 12:37 PM krzysztof@siejkowski.net via > swift-evolution <swift-evolution@swift.org> wrote:

+1. Way more readable than nested loops and consistent with already
existing syntax.

-----Original Message-----
From: Chris Eidhof via swift-evolution <swift-evolution@swift.org>
Reply: Chris Eidhof <chris@eidhof.nl>
Date: December 9, 2015 at 9:00:26 PM
To: swift-evolution@swift.org <swift-evolution@swift.org>
Subject: [swift-evolution] Extending the for loop to have multiple
clauses

> I think it could be really nice to extend the for-loop so that it can
have multiple clauses.
> Much like in the if-let with multiple clauses, I could imagine a
for-loop with multiple
> clauses:
>
> var cards: [(Suit,Rank)] = []
> for x in suits, y in ranks {
> cards.append((x,y))
> }
>
> This would be the same as writing:
>
> var cards: [(Suit,Rank)] = []
> for x in suits {
> for y in ranks {
> cards.append((x,y))}
> }
> }
>
> You could also do something like:
>
> for x in input1, y in (x..> // Do something with (x,y)
> }
>
> In fact, once we would have that, we could combine both if-let and for,
and make it more
> general, to end up with something like Haskell’s do-notation or C#’s
LINQ. But that might
> be taking it too far...
>
> Chris
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(thorsten@portableinnovations.de) #18

The advantage of not having to nest is that I can do all these nice monadic things with its. Just look at Scala's for-expression which does exactly that, mimicking Haskell's do-notation.
I would love support for that in Swift.

-Thorsten

···

Am 09.12.2015 um 23:11 schrieb Chris Eidhof via swift-evolution <swift-evolution@swift.org>:

Exactly! Just like multiple-if-let is the same as flatMap on optionals, this is a better syntax for flatMap on arrays.

To make it more clear that it’s a nested loop, we could also consider:

for x in xs, for y in ys {
}

But I’m not sure if it actually is clearer. (You could totally still interpret this as a zip, rather than a flatMap).

Chris

On 09 Dec 2015, at 16:51, Maxwell Swadling <maxs@apple.com> wrote:

On 9 Dec 2015, at 1:44 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 9, 2015, at 1:43 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

I think it could be really nice to extend the for-loop so that it can have multiple clauses. Much like in the if-let with multiple clauses, I could imagine a for-loop with multiple clauses:

var cards: [(Suit,Rank)] = []
for x in suits, y in ranks {
cards.append((x,y))
}

Isn’t this just

  for (x, y) in zip2(suits, ranks) {
  }

?

No, apparently it’s supposed to be all permutations of both types. But I too read it as syntactic sugar for a zip, which I think is a great reason not to add this syntax.

Ah. In that case, I’d much rather have a library function that indicates that we’re getting all permutations. This doesn’t feel like it belongs in the language at all, but in the library.

  - Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

We do have this library function, it is flatMap.

The desired feature here seems to be desugaring for loops

for x in xs, y in ys, z in zs {
  ...
}

into:

xs.flatMap { x in ys.flatMap { y in zs.map { z in ... } } }

So as above you would get:

suits.flatMap { x in ranks.map { y in cards.append((x,y)) } }

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(thorsten@portableinnovations.de) #19

Having a notation instead of just a function enables lots of sweet stuff not just nested loops (which happens within the List monad) but more, depending on the monad being used. Just have a look at Scala's for-expression, Haskell's do-notation or C#'s LINQ, e.g.

let result: Future<Int> = for
    x in getXAsynchronously(),
    y in getYAsynchronously() {
        x + y
    }

using an Async Monad here, where flatMap() is defined appropriately.

-Thorsten

···

Am 09.12.2015 um 23:38 schrieb Colin Barrett via swift-evolution <swift-evolution@swift.org>:

Indeed. In fact, ilya posted this already with an implementation upthread :slight_smile:

BTW, the mathematical term is “Cartesian product”, named so because this operation is how you construct the famous Cartesian xy-coordinate system we all learned in Algebra class.

FWIW I agree that this should be a library function and not in the language.

-Colin

On Dec 9, 2015, at 5:31 PM, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

You could have a function (lets call it cross for lack of me knowing a better mathematical term) that takes in multiple CollectionTypes and returns a CollectionType of all the combinations of the constituent sequence type elements.

for (x,y) in cross(0..<width, 0..<height) { … }

My swift-fu however is not yet strong enough for me to know how to avoid the backing generators being CrossGenerator2, CrossGenerator3, etc.

-DW

On Dec 9, 2015, at 3:11 PM, Chris Eidhof via swift-evolution <swift-evolution@swift.org> wrote:

Exactly! Just like multiple-if-let is the same as flatMap on optionals, this is a better syntax for flatMap on arrays.

To make it more clear that it’s a nested loop, we could also consider:

for x in xs, for y in ys {
}

But I’m not sure if it actually is clearer. (You could totally still interpret this as a zip, rather than a flatMap).

Chris

On 09 Dec 2015, at 16:51, Maxwell Swadling <maxs@apple.com> wrote:

On 9 Dec 2015, at 1:44 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 9, 2015, at 1:43 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

I think it could be really nice to extend the for-loop so that it can have multiple clauses. Much like in the if-let with multiple clauses, I could imagine a for-loop with multiple clauses:

var cards: [(Suit,Rank)] = []
for x in suits, y in ranks {
cards.append((x,y))
}

Isn’t this just

  for (x, y) in zip2(suits, ranks) {
  }

?

No, apparently it’s supposed to be all permutations of both types. But I too read it as syntactic sugar for a zip, which I think is a great reason not to add this syntax.

Ah. In that case, I’d much rather have a library function that indicates that we’re getting all permutations. This doesn’t feel like it belongs in the language at all, but in the library.

  - Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

We do have this library function, it is flatMap.

The desired feature here seems to be desugaring for loops

for x in xs, y in ys, z in zs {
  ...
}

into:

xs.flatMap { x in ys.flatMap { y in zs.map { z in ... } } }

So as above you would get:

suits.flatMap { x in ranks.map { y in cards.append((x,y)) } }

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Chris Eidhof) #20

I’ve thought a bit more about this, and now have changed my mind: I don’t think supporting multiple clauses in a for loop is a good idea anymore.

It’s confusing: people who’re not familiar with this concept from other languages expect a zip rather than a flatMap. I think the added value of having multiple clauses doesn’t really pay for this.

I still think there’s a lot of value in having something like do-notation, but I also feel that that’s too big a step currently.

Chris

···

On 10 Dec 2015, at 07:56, thorsten@portableinnovations.de wrote:

Having a notation instead of just a function enables lots of sweet stuff not just nested loops (which happens within the List monad) but more, depending on the monad being used. Just have a look at Scala's for-expression, Haskell's do-notation or C#'s LINQ, e.g.

let result: Future<Int> = for
    x in getXAsynchronously(),
    y in getYAsynchronously() {
        x + y
    }

using an Async Monad here, where flatMap() is defined appropriately.

-Thorsten

Am 09.12.2015 um 23:38 schrieb Colin Barrett via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

Indeed. In fact, ilya posted this already with an implementation upthread :slight_smile:

BTW, the mathematical term is “Cartesian product”, named so because this operation is how you construct the famous Cartesian xy-coordinate system we all learned in Algebra class.

FWIW I agree that this should be a library function and not in the language.

-Colin

On Dec 9, 2015, at 5:31 PM, David Waite via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

You could have a function (lets call it cross for lack of me knowing a better mathematical term) that takes in multiple CollectionTypes and returns a CollectionType of all the combinations of the constituent sequence type elements.

for (x,y) in cross(0..<width, 0..<height) { … }

My swift-fu however is not yet strong enough for me to know how to avoid the backing generators being CrossGenerator2, CrossGenerator3, etc.

-DW

On Dec 9, 2015, at 3:11 PM, Chris Eidhof via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Exactly! Just like multiple-if-let is the same as flatMap on optionals, this is a better syntax for flatMap on arrays.

To make it more clear that it’s a nested loop, we could also consider:

for x in xs, for y in ys {
}

But I’m not sure if it actually is clearer. (You could totally still interpret this as a zip, rather than a flatMap).

Chris

On 09 Dec 2015, at 16:51, Maxwell Swadling <maxs@apple.com <mailto:maxs@apple.com>> wrote:

On 9 Dec 2015, at 1:44 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Dec 9, 2015, at 1:43 PM, Brent Royal-Gordon <brent@architechies.com <mailto:brent@architechies.com>> wrote:

I think it could be really nice to extend the for-loop so that it can have multiple clauses. Much like in the if-let with multiple clauses, I could imagine a for-loop with multiple clauses:

var cards: [(Suit,Rank)] = []
for x in suits, y in ranks {
cards.append((x,y))
}

Isn’t this just

  for (x, y) in zip2(suits, ranks) {
  }

?

No, apparently it’s supposed to be all permutations of both types. But I too read it as syntactic sugar for a zip, which I think is a great reason not to add this syntax.

Ah. In that case, I’d much rather have a library function that indicates that we’re getting all permutations. This doesn’t feel like it belongs in the language at all, but in the library.

  - Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

We do have this library function, it is flatMap.

The desired feature here seems to be desugaring for loops

for x in xs, y in ys, z in zs {
  ...
}

into:

xs.flatMap { x in ys.flatMap { y in zs.map { z in ... } } }

So as above you would get:

suits.flatMap { x in ranks.map { y in cards.append((x,y)) } }

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution