Proposal: Add generator functions to the language

Looking for feedback on crafting a proposal adding generator functions to Swift. I understand this will likely be a very involved proposal at the language level, although I actually don’t know the complexity of the change within the Swift compiler itself.

This would be a function which returns multiple values, which is converted by the compiler to a function returning a SequenceType

A very basic syntax would be to add generator as a modifier to func, and likely involve a new keyword ‘yield’ to differentiate from the flow control behavior of ‘return’.

So for example:

generator func helloGenerator(name:String?) -> String {
    yield “Hello”
    yield name ?? “World”
}

Would have the following expected usage:

for str in helloGenerator(“David") {
   print str
// prints:
// Hello
// David
}

And for those unfamiliar to these sorts of simple cases, would have equivalent behavior to the following code:

45> func helloGenerator(name:String?) -> HelloGenerator {
46. return HelloGenerator(name)
47. }
48.
49. struct HelloGenerator : GeneratorType, SequenceType {
50. var position:Int = 0
51. let name:String?
52.
53. private init(_ name:String?) {
54. self.name = name
55. }
56.
57. func generator() -> HelloGenerator {
58. return self
59. }
60.
61. mutating func next() -> String? {
62. switch position {
63. case 0:
64. position = 1
65. return "Hello"
66. case 1:
67. position = 2
68. return name ?? "World"
69. default:
70. return nil
71. }
72. }
73. }

This syntax has at a bare minimum issues with generator closures and for a terse syntax for yielding over another sequence type within a generator function vs. using a loop. (possibly “yield in sequenceName”)

The interaction with the error system might involve disallowing throws from generator functions, or having the Element type be a Result<T> rather than T, as the GeneratorType next() method is not declared as throwing.

This could pair well to make for-in loops more comprehensive, especially if C-style for loops are eliminated.

This would possibly be a first step toward a coroutine-based concurrency system, although I am not proposing that sort of usage or scope here. The goal would be to emit an object compatible with SequenceType

-David Waite (DW)

+1.

I make extensive use of generators in Python, particularly for lazy evaluation, and their absence in Swift is unfortunate.

In a typical ARC-based iOS scenario

for photo in photoGenerator() {
    //something with photo
}

is ("should be specified to be") more memory efficient than

for photo in something.buildArrayOfPhotos() {
  //something with photo
}

which likely entails an OOM crash and is a common source of noob programmer error.

Reducing the friction of SequenceType in such cases is an improvement entirely orthogonal to coroutines (of which I am much more skeptical).

Drew

···

On Dec 11, 2015, at 6:21 PM, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

Looking for feedback on crafting a proposal adding generator functions to Swift. I understand this will likely be a very involved proposal at the language level, although I actually don’t know the complexity of the change within the Swift compiler itself.

This would be a function which returns multiple values, which is converted by the compiler to a function returning a SequenceType

A very basic syntax would be to add generator as a modifier to func, and likely involve a new keyword ‘yield’ to differentiate from the flow control behavior of ‘return’.

So for example:

generator func helloGenerator(name:String?) -> String {
    yield “Hello”
    yield name ?? “World”
}

Would have the following expected usage:

for str in helloGenerator(“David") {
   print str
// prints:
// Hello
// David
}

And for those unfamiliar to these sorts of simple cases, would have equivalent behavior to the following code:

45> func helloGenerator(name:String?) -> HelloGenerator {
46. return HelloGenerator(name)
47. }
48.
49. struct HelloGenerator : GeneratorType, SequenceType {
50. var position:Int = 0
51. let name:String?
52.
53. private init(_ name:String?) {
54. self.name = name
55. }
56.
57. func generator() -> HelloGenerator {
58. return self
59. }
60.
61. mutating func next() -> String? {
62. switch position {
63. case 0:
64. position = 1
65. return "Hello"
66. case 1:
67. position = 2
68. return name ?? "World"
69. default:
70. return nil
71. }
72. }
73. }

This syntax has at a bare minimum issues with generator closures and for a terse syntax for yielding over another sequence type within a generator function vs. using a loop. (possibly “yield in sequenceName”)

The interaction with the error system might involve disallowing throws from generator functions, or having the Element type be a Result<T> rather than T, as the GeneratorType next() method is not declared as throwing.

This could pair well to make for-in loops more comprehensive, especially if C-style for loops are eliminated.

This would possibly be a first step toward a coroutine-based concurrency system, although I am not proposing that sort of usage or scope here. The goal would be to emit an object compatible with SequenceType

-David Waite (DW)

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

I'd love to have first-class support for generators like this, but it's
a *lot* of work. It's a lot easier to do this sort of thing in a
scripting language like Python than it is to do in a language like
Swift, because it requires reifying the stack into a data structure that
can be passed around. And I suspect there's a lot of non-trivial
questions that have to get answered before you can even propose an
implementation for this.

For context, this sort of thing is something that people have been
talking about doing in Rust for quite a while, and it keeps getting
punted because of the amount of work and the unanswered questions about
how it would actually be implemented.

So I'll give this a general +1, but I think it should also be deferred
until after Swift 3 at the earliest.

-Kevin

···

On Fri, Dec 11, 2015, at 04:21 PM, David Waite via swift-evolution wrote:

Looking for feedback on crafting a proposal adding generator functions
to Swift. I understand this will likely be a very involved proposal at
the language level, although I actually don’t know the complexity of
the change within the Swift compiler itself.

This would be a function which returns multiple values, which is
converted by the compiler to a function returning a SequenceType

A very basic syntax would be to add generator as a modifier to func,
and likely involve a new keyword ‘yield’ to differentiate from the
flow control behavior of ‘return’.

So for example:

generator func helloGenerator(name:String?) -> String { yield
“Hello” yield name ?? “World” }

Would have the following expected usage:

for str in helloGenerator(“David") { print str // prints: //
Hello // David }

And for those unfamiliar to these sorts of simple cases, would have
equivalent behavior to the following code:

45> func helloGenerator(name:String?) -> HelloGenerator { 46.
return HelloGenerator(name) 47. } 48. 49. struct HelloGenerator :
GeneratorType, SequenceType { 50. var position:Int = 0 51. let
name:String? 52. 53. private init(_ name:String?) { 54.
self.name = name 55. } 56. 57. func generator() ->
HelloGenerator { 58. return self 59. } 60. 61. mutating
func next() -> String? { 62. switch position { 63. case
0: 64. position = 1 65. return "Hello" 66.
case 1: 67. position = 2 68. return name ??
"World" 69. default: 70. return nil 71. } 72.
} 73. }

This syntax has at a bare minimum issues with generator closures and
for a terse syntax for yielding over another sequence type within a
generator function vs. using a loop. (possibly “yield in
sequenceName”)

The interaction with the error system might involve disallowing throws
from generator functions, or having the Element type be a Result<T>
rather than T, as the GeneratorType next() method is not declared as
throwing.

This could pair well to make for-in loops more comprehensive,
especially if C-style for loops are eliminated.

This would possibly be a first step toward a coroutine-based
concurrency system, although I am not proposing that sort of usage or
scope here. The goal would be to emit an object compatible with
SequenceType

-David Waite (DW)

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

What exactly is the difference to just returning a sequence?

func helloGenerator(name : String?) -> [String] {
    return [
        "Hello",
        name ?? "World"
    ]
}

for str in helloGenerator("David") {
    print(str)
}

And if you want if lazy:

func helloGenerator(name : String?) -> LazyCollection<[String]> {
    return [
        "Hello",
        name ?? "World"
    ].lazy
}

Seems like exactly the same, except it doesn’t need two new keywords nor revamping of SequenceType/GeneratorType

···

On 12 Dec 2015, at 01:21, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

Looking for feedback on crafting a proposal adding generator functions to Swift. I understand this will likely be a very involved proposal at the language level, although I actually don’t know the complexity of the change within the Swift compiler itself.

This would be a function which returns multiple values, which is converted by the compiler to a function returning a SequenceType

A very basic syntax would be to add generator as a modifier to func, and likely involve a new keyword ‘yield’ to differentiate from the flow control behavior of ‘return’.

So for example:

generator func helloGenerator(name:String?) -> String {
    yield “Hello”
    yield name ?? “World”
}

Would have the following expected usage:

for str in helloGenerator(“David") {
   print str
// prints:
// Hello
// David
}

And for those unfamiliar to these sorts of simple cases, would have equivalent behavior to the following code:

45> func helloGenerator(name:String?) -> HelloGenerator {
46. return HelloGenerator(name)
47. }
48.
49. struct HelloGenerator : GeneratorType, SequenceType {
50. var position:Int = 0
51. let name:String?
52.
53. private init(_ name:String?) {
54. self.name = name
55. }
56.
57. func generator() -> HelloGenerator {
58. return self
59. }
60.
61. mutating func next() -> String? {
62. switch position {
63. case 0:
64. position = 1
65. return "Hello"
66. case 1:
67. position = 2
68. return name ?? "World"
69. default:
70. return nil
71. }
72. }
73. }

This syntax has at a bare minimum issues with generator closures and for a terse syntax for yielding over another sequence type within a generator function vs. using a loop. (possibly “yield in sequenceName”)

The interaction with the error system might involve disallowing throws from generator functions, or having the Element type be a Result<T> rather than T, as the GeneratorType next() method is not declared as throwing.

This could pair well to make for-in loops more comprehensive, especially if C-style for loops are eliminated.

This would possibly be a first step toward a coroutine-based concurrency system, although I am not proposing that sort of usage or scope here. The goal would be to emit an object compatible with SequenceType

-David Waite (DW)

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

Presumably, this could work with (or replace) GeneratorType, since all
generators as they currently exist could be implemented this way.

···

On Fri, Dec 11, 2015 at 4:36 PM, Drew Crawford via swift-evolution < swift-evolution@swift.org> wrote:

+1.

I make extensive use of generators in Python, particularly for lazy
evaluation, and their absence in Swift is unfortunate.

In a typical ARC-based iOS scenario

for photo in photoGenerator() {
    //something with photo
}

is ("should be specified to be") more memory efficient than

for photo in something.buildArrayOfPhotos() {
  //something with photo
}

which likely entails an OOM crash and is a common source of noob
programmer error.

Reducing the friction of SequenceType in such cases is an improvement
entirely orthogonal to coroutines (of which I am much more skeptical).

Drew

On Dec 11, 2015, at 6:21 PM, David Waite via swift-evolution < > swift-evolution@swift.org> wrote:

Looking for feedback on crafting a proposal adding generator functions to
Swift. I understand this will likely be a very involved proposal at the
language level, although I actually don’t know the complexity of the change
within the Swift compiler itself.

This would be a function which returns multiple values, which is converted
by the compiler to a function returning a SequenceType

A very basic syntax would be to add generator as a modifier to func, and
likely involve a new keyword ‘yield’ to differentiate from the flow control
behavior of ‘return’.

So for example:

generator func helloGenerator(name:String?) -> String {
    yield “Hello”
    yield name ?? “World”
}

Would have the following expected usage:

for str in helloGenerator(“David") {
   print str
// prints:

// Hello
// David

}

And for those unfamiliar to these sorts of simple cases, would have
equivalent behavior to the following code:

45> func helloGenerator(name:String?) -> HelloGenerator {
46. return HelloGenerator(name)
47. }
48.
49. struct HelloGenerator : GeneratorType, SequenceType {
50. var position:Int = 0
51. let name:String?
52.
53. private init(_ name:String?) {
54. self.name = name
55. }
56.
57. func generator() -> HelloGenerator {
58. return self
59. }
60.
61. mutating func next() -> String? {
62. switch position {
63. case 0:
64. position = 1
65. return "Hello"
66. case 1:
67. position = 2
68. return name ?? "World"
69. default:
70. return nil
71. }
72. }
73. }

This syntax has at a bare minimum issues with generator closures and for a
terse syntax for yielding over another sequence type within a generator
function vs. using a loop. (possibly “yield in sequenceName”)

The interaction with the error system might involve disallowing throws
from generator functions, or having the Element type be a Result<T> rather
than T, as the GeneratorType next() method is not declared as throwing.

This could pair well to make for-in loops more comprehensive, especially
if C-style for loops are eliminated.

This would possibly be a first step toward a coroutine-based concurrency
system, although I am not proposing that sort of usage or scope here. The
goal would be to emit an object compatible with SequenceType

-David Waite (DW)
_______________________________________________
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

What exactly is the difference to just returning a sequence?

func helloGenerator(name : String?) -> [String] {
    return [
        "Hello",
        name ?? "World"
    ]
}

for str in helloGenerator("David") {
    print(str)
}

And if you want if lazy:

func helloGenerator(name : String?) -> LazyCollection<[String]> {
    return [
        "Hello",
        name ?? "World"
    ].lazy
}

Imagine reading images from a directory and returning them lazily. I don’t think anybody is arguing that you cannot implement it now with a special lazy collection (your version actually materializes the array fully first) but here’s how simple it could be:

func readAllImagesInDirectory(path: String) -> GeneratorType<Image> {
  let imagePaths = listImagesInDirectory(path)
  return generator func generatorImage() -> Image {
    for imagePath in imagePaths {
      yield readImageAtPath(image
    }
  }
}

(Please forgive any errant syntax in my contrived example). Point is it could be a very fast/compact way to create generators; although it’s possible today.

···

On Dec 11, 2015, at 6:38 PM, Kametrixom Tikara via swift-evolution <swift-evolution@swift.org> wrote:

Seems like exactly the same, except it doesn’t need two new keywords nor revamping of SequenceType/GeneratorType

On 12 Dec 2015, at 01:21, David Waite via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Looking for feedback on crafting a proposal adding generator functions to Swift. I understand this will likely be a very involved proposal at the language level, although I actually don’t know the complexity of the change within the Swift compiler itself.

This would be a function which returns multiple values, which is converted by the compiler to a function returning a SequenceType

A very basic syntax would be to add generator as a modifier to func, and likely involve a new keyword ‘yield’ to differentiate from the flow control behavior of ‘return’.

So for example:

generator func helloGenerator(name:String?) -> String {
    yield “Hello”
    yield name ?? “World”
}

Would have the following expected usage:

for str in helloGenerator(“David") {
   print str
// prints:
// Hello
// David
}

And for those unfamiliar to these sorts of simple cases, would have equivalent behavior to the following code:

45> func helloGenerator(name:String?) -> HelloGenerator {
46. return HelloGenerator(name)
47. }
48.
49. struct HelloGenerator : GeneratorType, SequenceType {
50. var position:Int = 0
51. let name:String?
52.
53. private init(_ name:String?) {
54. self.name = name
55. }
56.
57. func generator() -> HelloGenerator {
58. return self
59. }
60.
61. mutating func next() -> String? {
62. switch position {
63. case 0:
64. position = 1
65. return "Hello"
66. case 1:
67. position = 2
68. return name ?? "World"
69. default:
70. return nil
71. }
72. }
73. }

This syntax has at a bare minimum issues with generator closures and for a terse syntax for yielding over another sequence type within a generator function vs. using a loop. (possibly “yield in sequenceName”)

The interaction with the error system might involve disallowing throws from generator functions, or having the Element type be a Result<T> rather than T, as the GeneratorType next() method is not declared as throwing.

This could pair well to make for-in loops more comprehensive, especially if C-style for loops are eliminated.

This would possibly be a first step toward a coroutine-based concurrency system, although I am not proposing that sort of usage or scope here. The goal would be to emit an object compatible with SequenceType

-David Waite (DW)

_______________________________________________
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

One difference is the memory usage:

func helloGenerator() -> LazyCollection<[String]> {
    return [
        1GBAllocation(),
        1GBAllocation()
    ].lazy
}

vs

func helloGenerator() -> SequenceType {
  yield 1GBAllocation()
  //allocation released
  yield 1GBAllocation()
}

···

On Dec 11, 2015, at 7:38 PM, Kametrixom Tikara via swift-evolution <swift-evolution@swift.org> wrote:

What exactly is the difference to just returning a sequence?

func helloGenerator(name : String?) -> [String] {
    return [
        "Hello",
        name ?? "World"
    ]
}

for str in helloGenerator("David") {
    print(str)
}

And if you want if lazy:

func helloGenerator(name : String?) -> LazyCollection<[String]> {
    return [
        "Hello",
        name ?? "World"
    ].lazy
}

Seems like exactly the same, except it doesn’t need two new keywords nor revamping of SequenceType/GeneratorType

On 12 Dec 2015, at 01:21, David Waite via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Looking for feedback on crafting a proposal adding generator functions to Swift. I understand this will likely be a very involved proposal at the language level, although I actually don’t know the complexity of the change within the Swift compiler itself.

This would be a function which returns multiple values, which is converted by the compiler to a function returning a SequenceType

A very basic syntax would be to add generator as a modifier to func, and likely involve a new keyword ‘yield’ to differentiate from the flow control behavior of ‘return’.

So for example:

generator func helloGenerator(name:String?) -> String {
    yield “Hello”
    yield name ?? “World”
}

Would have the following expected usage:

for str in helloGenerator(“David") {
   print str
// prints:
// Hello
// David
}

And for those unfamiliar to these sorts of simple cases, would have equivalent behavior to the following code:

45> func helloGenerator(name:String?) -> HelloGenerator {
46. return HelloGenerator(name)
47. }
48.
49. struct HelloGenerator : GeneratorType, SequenceType {
50. var position:Int = 0
51. let name:String?
52.
53. private init(_ name:String?) {
54. self.name = name
55. }
56.
57. func generator() -> HelloGenerator {
58. return self
59. }
60.
61. mutating func next() -> String? {
62. switch position {
63. case 0:
64. position = 1
65. return "Hello"
66. case 1:
67. position = 2
68. return name ?? "World"
69. default:
70. return nil
71. }
72. }
73. }

This syntax has at a bare minimum issues with generator closures and for a terse syntax for yielding over another sequence type within a generator function vs. using a loop. (possibly “yield in sequenceName”)

The interaction with the error system might involve disallowing throws from generator functions, or having the Element type be a Result<T> rather than T, as the GeneratorType next() method is not declared as throwing.

This could pair well to make for-in loops more comprehensive, especially if C-style for loops are eliminated.

This would possibly be a first step toward a coroutine-based concurrency system, although I am not proposing that sort of usage or scope here. The goal would be to emit an object compatible with SequenceType

-David Waite (DW)

_______________________________________________
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

It is tough to give examples which are suitably approachable, illustrative, and complex. Perhaps the Fibonacci sequence:

generator func fibonacci() -> Int {
   var = 0, j = 1
   repeat {
      (i, j) = (j, i + j)
      yield i
   }
}

In addition to being a more complex problem, the sequence is also infinite. Luckily the functional influences on sequences let you deal with that without issue:

// print the first 25 numbers in the fibonacci sequence
fibonacci().prefix(25).forEach { print($0) }

-DW

···

On Dec 11, 2015, at 6:38 PM, Kametrixom Tikara <kametrixom@icloud.com> wrote:

What exactly is the difference to just returning a sequence?

func helloGenerator(name : String?) -> [String] {
    return [
        "Hello",
        name ?? "World"
    ]
}

for str in helloGenerator("David") {
    print(str)
}

And if you want if lazy:

func helloGenerator(name : String?) -> LazyCollection<[String]> {
    return [
        "Hello",
        name ?? "World"
    ].lazy
}

My mistake, here is a truly lazy version (it gets a little ugly yes):

func helloGenerator(name : String?) -> LazyMapCollection<[() -> String], String> {
    return [
        { "Hello" },
        { name ?? "World" }
    ].lazy.map{ $0() }
}

I agree that this would make it very convenient. I’m +.5 on this, because I don’t like adding two more keywords for a single feature, but it seems like it has good applications

···

On 12 Dec 2015, at 02:50, Kevin Wooten <kdubb@me.com> wrote:

On Dec 11, 2015, at 6:38 PM, Kametrixom Tikara via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

What exactly is the difference to just returning a sequence?

func helloGenerator(name : String?) -> [String] {
    return [
        "Hello",
        name ?? "World"
    ]
}

for str in helloGenerator("David") {
    print(str)
}

And if you want if lazy:

func helloGenerator(name : String?) -> LazyCollection<[String]> {
    return [
        "Hello",
        name ?? "World"
    ].lazy
}

Imagine reading images from a directory and returning them lazily. I don’t think anybody is arguing that you cannot implement it now with a special lazy collection (your version actually materializes the array fully first) but here’s how simple it could be:

func readAllImagesInDirectory(path: String) -> GeneratorType<Image> {
  let imagePaths = listImagesInDirectory(path)
  return generator func generatorImage() -> Image {
    for imagePath in imagePaths {
      yield readImageAtPath(image
    }
  }
}

(Please forgive any errant syntax in my contrived example). Point is it could be a very fast/compact way to create generators; although it’s possible today.

Seems like exactly the same, except it doesn’t need two new keywords nor revamping of SequenceType/GeneratorType

On 12 Dec 2015, at 01:21, David Waite via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Looking for feedback on crafting a proposal adding generator functions to Swift. I understand this will likely be a very involved proposal at the language level, although I actually don’t know the complexity of the change within the Swift compiler itself.

This would be a function which returns multiple values, which is converted by the compiler to a function returning a SequenceType

A very basic syntax would be to add generator as a modifier to func, and likely involve a new keyword ‘yield’ to differentiate from the flow control behavior of ‘return’.

So for example:

generator func helloGenerator(name:String?) -> String {
    yield “Hello”
    yield name ?? “World”
}

Would have the following expected usage:

for str in helloGenerator(“David") {
   print str
// prints:
// Hello
// David
}

And for those unfamiliar to these sorts of simple cases, would have equivalent behavior to the following code:

45> func helloGenerator(name:String?) -> HelloGenerator {
46. return HelloGenerator(name)
47. }
48.
49. struct HelloGenerator : GeneratorType, SequenceType {
50. var position:Int = 0
51. let name:String?
52.
53. private init(_ name:String?) {
54. self.name = name
55. }
56.
57. func generator() -> HelloGenerator {
58. return self
59. }
60.
61. mutating func next() -> String? {
62. switch position {
63. case 0:
64. position = 1
65. return "Hello"
66. case 1:
67. position = 2
68. return name ?? "World"
69. default:
70. return nil
71. }
72. }
73. }

This syntax has at a bare minimum issues with generator closures and for a terse syntax for yielding over another sequence type within a generator function vs. using a loop. (possibly “yield in sequenceName”)

The interaction with the error system might involve disallowing throws from generator functions, or having the Element type be a Result<T> rather than T, as the GeneratorType next() method is not declared as throwing.

This could pair well to make for-in loops more comprehensive, especially if C-style for loops are eliminated.

This would possibly be a first step toward a coroutine-based concurrency system, although I am not proposing that sort of usage or scope here. The goal would be to emit an object compatible with SequenceType

-David Waite (DW)

_______________________________________________
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

Right. This is a major add-on feature that doesn’t fit in with the stated goals for Swift 3 (README of https://github.com/apple/swift-evolution\), so I think we should proactively defer it.

  - Doug

···

On Dec 11, 2015, at 10:48 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

I'd love to have first-class support for generators like this, but it's a lot of work. It's a lot easier to do this sort of thing in a scripting language like Python than it is to do in a language like Swift, because it requires reifying the stack into a data structure that can be passed around. And I suspect there's a lot of non-trivial questions that have to get answered before you can even propose an implementation for this.

For context, this sort of thing is something that people have been talking about doing in Rust for quite a while, and it keeps getting punted because of the amount of work and the unanswered questions about how it would actually be implemented.

So I'll give this a general +1, but I think it should also be deferred until after Swift 3 at the earliest.

I'm still not sold on the generate keyword; this syntax (once it works)
seems clear enough for me:

func fibonacci() -> SequenceType<Int> {
   var (i, j) = (0, 1)
   repeat {
      (i, j) = (j, i + j)
      yield i
   }
}

The intent is hard to miss: this function returns something that satisfies
a specialization of SequenceType to type Int. The function can do this
either

(1) by returning a valid SequenceType struct/class object,
(2) by doing a simple return, in which case compiler impements an empty
sequence behind the scenes, or
(3) by inserting a few yield operators before doing (1) or (2), in which
case the compiler creates an appropriate generator struct

Whether the function goes the route (1), (2) or (3) is an implementation
detail of no import to its users.

···

On Sat, Dec 12, 2015 at 8:46 AM, David Waite via swift-evolution < swift-evolution@swift.org> wrote:

It is tough to give examples which are suitably approachable,
illustrative, and complex. Perhaps the Fibonacci sequence:

generator func fibonacci() -> Int {
   var = 0, j = 1
   repeat {
      (i, j) = (j, i + j)
      yield i
   }
}

In addition to being a more complex problem, the sequence is also
infinite. Luckily the functional influences on sequences let you deal with
that without issue:

// print the first 25 numbers in the fibonacci sequence

fibonacci().prefix(25).forEach { print($0) }

-DW

On Dec 11, 2015, at 6:38 PM, Kametrixom Tikara <kametrixom@icloud.com> > wrote:

What exactly is the difference to just returning a sequence?

func helloGenerator(name : String?) -> [String] {
    return [
        "Hello",
        name ?? "World"
    ]
}

for str in helloGenerator("David") {
    print(str)
}

And if you want if lazy:

func helloGenerator(name : String?) -> LazyCollection<[String]> {
    return [
        "Hello",
        name ?? "World"
    ].lazy
}

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

I’d be more than happy with alternative syntax (and would be surprised if there aren’t several proposals from this list worth consideration), but thats perhaps more of a bike shed issue compared to whether or not the feature itself is desired and in scope.

-DW

···

On Dec 11, 2015, at 6:58 PM, Kametrixom Tikara <kametrixom@icloud.com> wrote:

I agree that this would make it very convenient. I’m +.5 on this, because I don’t like adding two more keywords for a single feature, but it seems like it has good applications

Do we actually need the "generator" keyword? Why can't the compiler figure it out by the presence of yield?

And "yield" is a fairly standard keyword across languages, so no harm in claiming it, IMO.

+1 on the proposal.

A.

I proposed it because
- Only a few named language features (like Concurrency) were explicit non-goals
- Its a feature I’m willing to try to implement (although I would probably want to work on several smaller changes to get up to speed first on SIL and the codebase)

Is there a possibility for larger features like this or macros to be done in parallel with the 2.2/3.0 development effort? Or perhaps a desire to have this sort of working group approach (similar to C++’s Technical Subcommittees), but not until 3.0 is released?

-DW

···

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

On Dec 11, 2015, at 10:48 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I'd love to have first-class support for generators like this, but it's a lot of work. It's a lot easier to do this sort of thing in a scripting language like Python than it is to do in a language like Swift, because it requires reifying the stack into a data structure that can be passed around. And I suspect there's a lot of non-trivial questions that have to get answered before you can even propose an implementation for this.

For context, this sort of thing is something that people have been talking about doing in Rust for quite a while, and it keeps getting punted because of the amount of work and the unanswered questions about how it would actually be implemented.

So I'll give this a general +1, but I think it should also be deferred until after Swift 3 at the earliest.

Right. This is a major add-on feature that doesn’t fit in with the stated goals for Swift 3 (README of https://github.com/apple/swift-evolution\), so I think we should proactively defer it.

  - Doug

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

I agree with both of you. I’m very interested in this, but it is clearly out of scope for Swift 3. It should also be considered alongside whatever async/concurrency approach we tackle (likely in swift 4).

-Chris

···

On Dec 12, 2015, at 12:35 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 11, 2015, at 10:48 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I'd love to have first-class support for generators like this, but it's a lot of work. It's a lot easier to do this sort of thing in a scripting language like Python than it is to do in a language like Swift, because it requires reifying the stack into a data structure that can be passed around. And I suspect there's a lot of non-trivial questions that have to get answered before you can even propose an implementation for this.

For context, this sort of thing is something that people have been talking about doing in Rust for quite a while, and it keeps getting punted because of the amount of work and the unanswered questions about how it would actually be implemented.

So I'll give this a general +1, but I think it should also be deferred until after Swift 3 at the earliest.

Right. This is a major add-on feature that doesn’t fit in with the stated goals for Swift 3 (README of https://github.com/apple/swift-evolution\), so I think we should proactively defer it.

I think you need some explicit marker. Consider the following:

    func f() -> Int {
        return
    }

Is this (1) a generator that returns without yielding anything, or (2) an ordinary function that incorrectly fails to return a value?

···

On Dec 11, 2015, at 6:03 PM, Andrey Tarantsov via swift-evolution <swift-evolution@swift.org> wrote:

Do we actually need the "generator" keyword? Why can't the compiler figure it out by the presence of yield?

--
Greg Parker gparker@apple.com Runtime Wrangler

It's desireable for the signature of a function to be fully checkable without looking at the body, for a number of reasons. It's also something clients of a library will want to know, so it needs to be displayed in the library's interface somehow.

Jordan

···

On Dec 11, 2015, at 18:03 , Andrey Tarantsov via swift-evolution <swift-evolution@swift.org> wrote:

Do we actually need the "generator" keyword? Why can't the compiler figure it out by the presence of yield?

And "yield" is a fairly standard keyword across languages, so no harm in claiming it, IMO.

+1 on the proposal.

A.

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

Another possible syntax, inspired by "throws".

func foo() yield -> String

I'm not sure what happens if a function both yields and throws. I guess that can't happen right now because generators are required not to throw when producing their next() value.

Jordan

···

On Dec 11, 2015, at 18:14 , Jordan Rose <jordan_rose@apple.com> wrote:

It's desireable for the signature of a function to be fully checkable without looking at the body, for a number of reasons. It's also something clients of a library will want to know, so it needs to be displayed in the library's interface somehow.

Jordan

On Dec 11, 2015, at 18:03 , Andrey Tarantsov via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Do we actually need the "generator" keyword? Why can't the compiler figure it out by the presence of yield?

And "yield" is a fairly standard keyword across languages, so no harm in claiming it, IMO.

+1 on the proposal.

A.

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

Eh, I was trying to avoid grabbing another keyword, but I guess it's context-sensitive anyway.

···

On Dec 11, 2015, at 18:26 , Jonathan Hise Kaldma <info@hisekaldma.com> wrote:

Shouldn't it be:

func foo() yields -> String

Also +1 on the idea.

/Jonathan

12 dec. 2015 kl. 03:18 skrev Jordan Rose via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

Another possible syntax, inspired by "throws".

func foo() yield -> String

I'm not sure what happens if a function both yields and throws. I guess that can't happen right now because generators are required not to throw when producing their next() value.

Jordan

On Dec 11, 2015, at 18:14 , Jordan Rose <jordan_rose@apple.com <mailto:jordan_rose@apple.com>> wrote:

It's desireable for the signature of a function to be fully checkable without looking at the body, for a number of reasons. It's also something clients of a library will want to know, so it needs to be displayed in the library's interface somehow.

Jordan

On Dec 11, 2015, at 18:03 , Andrey Tarantsov via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Do we actually need the "generator" keyword? Why can't the compiler figure it out by the presence of yield?

And "yield" is a fairly standard keyword across languages, so no harm in claiming it, IMO.

+1 on the proposal.

A.

_______________________________________________
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

I've thought about this some. It might not have to be a keyword, if this were a generalized language feature. Anything that interrupts control flow and optionally resumes it later, such as 'throws', 'yields', and potentially also 'async', could be implemented as instances of algebraic effects. As a rough sketch of an idea, you could declare an effect and its operations:

effect throws { @noreturn operation throw (ErrorType) -> () }
effect yields<T> { operation yield (T) -> () }
effect awaits { operation await<T> (Promise<T>) -> T }

and 'catch' could be generalized to let you handle any effect operations that might be performed in the body of a block:

class Generator<T> {
  var generator: () yields<T> -> ()
  func next() -> T? {
    do {
        generator()
      return nil
    } catch yield (let x) {
      generator = currentContinuation
      return x
    }
  }
}

See Eff (http://www.eff-lang.org) for an example of a language with this already implemented.

-Joe

···

On Dec 11, 2015, at 6:26 PM, Jordan Rose via swift-evolution <swift-evolution@swift.org> wrote:

Eh, I was trying to avoid grabbing another keyword, but I guess it's context-sensitive anyway.

2 Likes