Streamlining closures


(Taras Zakharko) #1

Dear all,

first of all, thank you for giving the community the opportunity to influence the future of Swift. This is a strong move, which shows how passionate the Swift team is about their language and how you guys want to make it the best.

Personally, I am a huge fan of streamlined, logically concise concepts as well as syntax in programming languages. This is an area where Swift both excels at (love what you do with types in the compiler) but also sometimes lacking, as there seems to be a lot of idiosyncrasy, as evident from the huge number of language keywords and compiler attributes. Here, I would like to discuss some of the idiosyncrasy with closures. As I am trying to throw some random ideas out there, my post might be a bit chaotic, so please bear with me. Besides, I literally started playing around with Swift only few hours ago (I know, I know, I’m late), so my practical knowledge of the language is very limited.

1. I always wondered about the specific closure syntax { A -> B in … }. The type annotation is within the block for some reason and the there is the keyword ‘in’, which is usually associated with the for loop. My proposal would be to change this to simply:

   A -> B { … }

That is, the closure declaration closely mirrors the function declaration, but without the 'func name’ part. This is far from being just a cosmetic change though. Rather, it has some far-reaching consequences to how the concept of closure is applied to the language, but I believe that this would make Swift much more streamlined and concise (and also solve some other issues in the process).

The crucial point is that the type annotation is (and should be) optional. Which means that every code block { … } is a closure. I think this fundamentally makes a lot of sense. So, a function declaration is just a closure associated with a name identifier (aka. injected into the namespace). Similarly, code blocks used in statements such as for and do have local lexical scope, which makes them essentially compatible closures as far as I can see.

Essentially, this approach radically simplifies the language both from the syntax standpoint — closures and funcs are using the same syntax, the notion of code blocks is removed from the language; but also from the semantics standpoint. For instance, one can give more formal definitions for statements, which opens the path of treating statements as expressions (see https://lists.swift.org/pipermail/swift-evolution/2015-December/000133.html). For instance, current if is of type (Bool, ()->())->(), but it can easily be redeclared as (Bool, ()->T)->T, which opens way to nice things like

  let x = if(…) { .. some complex setup code..; value1} else { … other code..; value2}

Similarly, do can be used to incapsulate auxiliary computations in the local scope (see below).

2. Another closure limitation is that return type inference does not work with longer closures (but they should). Here a particular use case. Due to my work, I program a lot with R (which is sometimes a neat language in its own right). A particular paradigm I find very useful is to use anonymous code blocks in local scope to prevent namespace pollution, e.g. (in R code)

dataset <- local({
   a <- some_computation
   b <- some_computation

   a combined with b
})

// a and b are not visible in the outer scope anymore

In JavaScript, people often use anonymous functions for the same effect. The nice thing about this paradigm is that it allows one to properly isolate data dependency from each other, give the compiler a better sense of variable lifespan and overall make your code much nicer to read. Of course, in Swift this can be done with nested functions or closures, but its the question of aesthetics. At any rate, the R function is very easy to emulate in Swift:

func local<T>(closure: () -> T) -> T {
     return closure()
}

which works beautifully, e.g.

let (a1,a2) = local({return(1, 2)}

However, if I want to use more complex code, I need to explicitly declare the closure type:

   let (a1, a2) = local({ () -> (Int, Int) in
     let x2 = x*2

     return (x2, x2+1)
   })

This is bulky, and frankly, unnecessary, as the compiler can easily infer the return type of the closure.

BTW, combined with proposal in 1., this style of programming just becomes the beautiful

let (a1, a2) = do {
  .. do something ..
return(…, …) // or simply (…, …)
}

Summary TL;DR version of the proposal:

replace the {A->B in …} closure syntax by A->B {…} — where type declaration is optional, remove the concept of code block (replace it by closure) and statements that take blocks/closures typed expressions. Improve type inference for return values. Current syntactic sugar for closures such as anonymous arguments and last line as return values should be preserved.

Advantages: streamlines the language, solves a number of issues at the same time (idiosyncrasy, operators as expressions). In the end, makes the language easier to use and also to understand conceptually.

Disadvantages: I honestly can’t see a single one

Implementation challenge: would requires a parser change that might be non-trivial. I can also imagine that a lot of refactoring in the compiler code is necessary in regards to how statements work — but this might pay off as in the end, the compiler only needs to deal with closures and calls, which simplifies optimisation. And Swift is already great at aggressive inlining of closures.

Impact on existing code: the syntax of closure declarations can be automatically ported as the rewrite rule is trivial. As far as I can see, changing code blocks to closures won’t have any impact on the semantics of the control structures, because the blocks are already using lexical scope.

Eager to hear your thoughts on this!

Cheers,

Taras


(Thorsten Seitz) #2

Hi Taras,

I don't think the syntax { x in ... } is a hindrance for your proposal of turning all code blocks into closures, as all code blocks currently have no arguments and in that case both variants look the same. All code blocks with the exception of function bodies but I think having these looking different is ok.

An advantage of the current syntax is that you can leave off the parentheses when calling a closure if it is the last argument which already enables the syntax you would like for "local":

let x = local {
        ...
}

This requires having the arguments inside the braces to allow for things like these:

someCollection.forEach { x in ... }
File.open("some/path") { file in ... } // having the file closed automatically

Such usage patterns would look awkward with arguments outside of the braces and they are very common and very useful.

Actually I was very surprised to see that your example with "local" returning a tuple did not work. The type inferencer should be able to infer the result type T to be a tuple without problems.

-Thorsten

···

Am 13.12.2015 um 05:14 schrieb Taras Zakharko via swift-evolution <swift-evolution@swift.org>:

Dear all,

first of all, thank you for giving the community the opportunity to influence the future of Swift. This is a strong move, which shows how passionate the Swift team is about their language and how you guys want to make it the best.

Personally, I am a huge fan of streamlined, logically concise concepts as well as syntax in programming languages. This is an area where Swift both excels at (love what you do with types in the compiler) but also sometimes lacking, as there seems to be a lot of idiosyncrasy, as evident from the huge number of language keywords and compiler attributes. Here, I would like to discuss some of the idiosyncrasy with closures. As I am trying to throw some random ideas out there, my post might be a bit chaotic, so please bear with me. Besides, I literally started playing around with Swift only few hours ago (I know, I know, I’m late), so my practical knowledge of the language is very limited.

1. I always wondered about the specific closure syntax { A -> B in … }. The type annotation is within the block for some reason and the there is the keyword ‘in’, which is usually associated with the for loop. My proposal would be to change this to simply:

   A -> B { … }

That is, the closure declaration closely mirrors the function declaration, but without the 'func name’ part. This is far from being just a cosmetic change though. Rather, it has some far-reaching consequences to how the concept of closure is applied to the language, but I believe that this would make Swift much more streamlined and concise (and also solve some other issues in the process).

The crucial point is that the type annotation is (and should be) optional. Which means that every code block { … } is a closure. I think this fundamentally makes a lot of sense. So, a function declaration is just a closure associated with a name identifier (aka. injected into the namespace). Similarly, code blocks used in statements such as for and do have local lexical scope, which makes them essentially compatible closures as far as I can see.

Essentially, this approach radically simplifies the language both from the syntax standpoint — closures and funcs are using the same syntax, the notion of code blocks is removed from the language; but also from the semantics standpoint. For instance, one can give more formal definitions for statements, which opens the path of treating statements as expressions (see https://lists.swift.org/pipermail/swift-evolution/2015-December/000133.html). For instance, current if is of type (Bool, ()->())->(), but it can easily be redeclared as (Bool, ()->T)->T, which opens way to nice things like

  let x = if(…) { .. some complex setup code..; value1} else { … other code..; value2}

Similarly, do can be used to incapsulate auxiliary computations in the local scope (see below).

2. Another closure limitation is that return type inference does not work with longer closures (but they should). Here a particular use case. Due to my work, I program a lot with R (which is sometimes a neat language in its own right). A particular paradigm I find very useful is to use anonymous code blocks in local scope to prevent namespace pollution, e.g. (in R code)

dataset <- local({
   a <- some_computation
   b <- some_computation

   a combined with b
})

// a and b are not visible in the outer scope anymore

In JavaScript, people often use anonymous functions for the same effect. The nice thing about this paradigm is that it allows one to properly isolate data dependency from each other, give the compiler a better sense of variable lifespan and overall make your code much nicer to read. Of course, in Swift this can be done with nested functions or closures, but its the question of aesthetics. At any rate, the R function is very easy to emulate in Swift:

func local<T>(closure: () -> T) -> T {
     return closure()
}

which works beautifully, e.g.

let (a1,a2) = local({return(1, 2)}

However, if I want to use more complex code, I need to explicitly declare the closure type:

   let (a1, a2) = local({ () -> (Int, Int) in
     let x2 = x*2

     return (x2, x2+1)
   })

This is bulky, and frankly, unnecessary, as the compiler can easily infer the return type of the closure.

BTW, combined with proposal in 1., this style of programming just becomes the beautiful

let (a1, a2) = do {
  .. do something ..
return(…, …) // or simply (…, …)
}

Summary TL;DR version of the proposal:

replace the {A->B in …} closure syntax by A->B {…} — where type declaration is optional, remove the concept of code block (replace it by closure) and statements that take blocks/closures typed expressions. Improve type inference for return values. Current syntactic sugar for closures such as anonymous arguments and last line as return values should be preserved.

Advantages: streamlines the language, solves a number of issues at the same time (idiosyncrasy, operators as expressions). In the end, makes the language easier to use and also to understand conceptually.

Disadvantages: I honestly can’t see a single one

Implementation challenge: would requires a parser change that might be non-trivial. I can also imagine that a lot of refactoring in the compiler code is necessary in regards to how statements work — but this might pay off as in the end, the compiler only needs to deal with closures and calls, which simplifies optimisation. And Swift is already great at aggressive inlining of closures.

Impact on existing code: the syntax of closure declarations can be automatically ported as the rewrite rule is trivial. As far as I can see, changing code blocks to closures won’t have any impact on the semantics of the control structures, because the blocks are already using lexical scope.

Eager to hear your thoughts on this!

Cheers,

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


(Jordan Rose) #3

Hi, Taras. Thanks for writing this up. Unfortunately, it's infeasible to do most of these things, for several reasons.

Dear all,

first of all, thank you for giving the community the opportunity to influence the future of Swift. This is a strong move, which shows how passionate the Swift team is about their language and how you guys want to make it the best.

Personally, I am a huge fan of streamlined, logically concise concepts as well as syntax in programming languages. This is an area where Swift both excels at (love what you do with types in the compiler) but also sometimes lacking, as there seems to be a lot of idiosyncrasy, as evident from the huge number of language keywords and compiler attributes. Here, I would like to discuss some of the idiosyncrasy with closures. As I am trying to throw some random ideas out there, my post might be a bit chaotic, so please bear with me. Besides, I literally started playing around with Swift only few hours ago (I know, I know, I’m late), so my practical knowledge of the language is very limited.

1. I always wondered about the specific closure syntax { A -> B in … }. The type annotation is within the block for some reason and the there is the keyword ‘in’, which is usually associated with the for loop. My proposal would be to change this to simply:

   A -> B { … }

That is, the closure declaration closely mirrors the function declaration, but without the 'func name’ part. This is far from being just a cosmetic change though. Rather, it has some far-reaching consequences to how the concept of closure is applied to the language, but I believe that this would make Swift much more streamlined and concise (and also solve some other issues in the process).

We thought a lot about this, and settled on the current syntax (inside the braces) for several reasons, the main one being that it's much easier to parse. Without this, the compiler would have to stop whatever it's currently doing when it sees '->'. We do already do some of this work for things like "[Int]()" (which starts out as an array literal and then gets reinterpreted as a type later), but it leads to weird errors when the code is invalid or incomplete.

The keyword "in" was chosen because (a) it's already a keyword, (b) it's still short, and (c) it felt more Swifty than Smalltalk/Ruby pipes.

The crucial point is that the type annotation is (and should be) optional. Which means that every code block { … } is a closure. I think this fundamentally makes a lot of sense. So, a function declaration is just a closure associated with a name identifier (aka. injected into the namespace). Similarly, code blocks used in statements such as for and do have local lexical scope, which makes them essentially compatible closures as far as I can see.

Today, 'return' from a closure cannot return from the containing function. That alone would be a big language change.

Essentially, this approach radically simplifies the language both from the syntax standpoint — closures and funcs are using the same syntax, the notion of code blocks is removed from the language; but also from the semantics standpoint. For instance, one can give more formal definitions for statements, which opens the path of treating statements as expressions (see https://lists.swift.org/pipermail/swift-evolution/2015-December/000133.html). For instance, current if is of type (Bool, ()->())->(), but it can easily be redeclared as (Bool, ()->T)->T, which opens way to nice things like

  let x = if(…) { .. some complex setup code..; value1} else { … other code..; value2}

Similarly, do can be used to incapsulate auxiliary computations in the local scope (see below).

As discussed on the other thread, it's not at all clear that this is desireable. The implicit return in particular would be controversial <http://awardwinningfjords.com/2012/05/08/beware-coffeescript-comprehensions.html>.

2. Another closure limitation is that return type inference does not work with longer closures (but they should). Here a particular use case. Due to my work, I program a lot with R (which is sometimes a neat language in its own right). A particular paradigm I find very useful is to use anonymous code blocks in local scope to prevent namespace pollution, e.g. (in R code)

dataset <- local({
   a <- some_computation
   b <- some_computation

   a combined with b
})

// a and b are not visible in the outer scope anymore

In JavaScript, people often use anonymous functions for the same effect. The nice thing about this paradigm is that it allows one to properly isolate data dependency from each other, give the compiler a better sense of variable lifespan and overall make your code much nicer to read. Of course, in Swift this can be done with nested functions or closures, but its the question of aesthetics. At any rate, the R function is very easy to emulate in Swift:

func local<T>(closure: () -> T) -> T {
     return closure()
}

which works beautifully, e.g.

let (a1,a2) = local({return(1, 2)}

However, if I want to use more complex code, I need to explicitly declare the closure type:

   let (a1, a2) = local({ () -> (Int, Int) in
     let x2 = x*2

     return (x2, x2+1)
   })

This is bulky, and frankly, unnecessary, as the compiler can easily infer the return type of the closure.

BTW, combined with proposal in 1., this style of programming just becomes the beautiful

let (a1, a2) = do {
  .. do something ..
return(…, …) // or simply (…, …)
}

Swift's type inference is currently statement-oriented, so there's no easy way to do this inference. This is at least partly a compilation-time concern: Swift's type system allows many more possible conversions than, say, Haskell or OCaml, so solving the types for an entire multi-statement function is not a trivial problem, possibly not a tractable problem.

The canonical way to write your closure would be this:

  let (a1, a2): (Int, Int) = {
    let x2 = x*2
    return (x2, x2+1)
  }()

To summarize, I think Swift has made the right trade-offs for a statically-typed language that prefers clarity over brevity <https://swift.org/documentation/api-design-guidelines.html#fundamentals>. Simply re-syntaxing closures isn't out of the question, but we ultimately went for something that handles many inline uses with varying amounts of type info rather than trying to stick as closely as possible to function syntax.

Best,
Jordan

···

On Dec 12, 2015, at 20:14 , Taras Zakharko via swift-evolution <swift-evolution@swift.org> wrote:


(Marc Knaup) #4

How would you write this with the new syntax?

someAsyncFunction() { error in
    // …
}

This doesn't make sense (like Thorsten explained earlier):

someAsyncFunction() error {
    // …
}

I agree though that "in" is confusing and I never understood what "in"
means here.
Since "do" is no longer used for loops but code blocks it would make sense
to replace "in" with "do".

someAsyncFunction() { error do
    // …
}

···

On Sun, Dec 13, 2015 at 9:42 AM, Thorsten Seitz via swift-evolution < swift-evolution@swift.org> wrote:

Hi Taras,

I don't think the syntax { x in ... } is a hindrance for your proposal of
turning all code blocks into closures, as all code blocks currently have no
arguments and in that case both variants look the same. All code blocks
with the exception of function bodies but I think having these looking
different is ok.

An advantage of the current syntax is that you can leave off the
parentheses when calling a closure if it is the last argument which already
enables the syntax you would like for "local":

let x = local {
        ...
}

This requires having the arguments inside the braces to allow for things
like these:

someCollection.forEach { x in ... }
File.open("some/path") { file in ... } // having the file closed
automatically

Such usage patterns would look awkward with arguments outside of the
braces and they are very common and very useful.

Actually I was very surprised to see that your example with "local"
returning a tuple did not work. The type inferencer should be able to infer
the result type T to be a tuple without problems.

-Thorsten

Am 13.12.2015 um 05:14 schrieb Taras Zakharko via swift-evolution < > swift-evolution@swift.org>:

Dear all,

first of all, thank you for giving the community the opportunity to
influence the future of Swift. This is a strong move, which shows how
passionate the Swift team is about their language and how you guys want to
make it the best.

Personally, I am a huge fan of streamlined, logically concise concepts as
well as syntax in programming languages. This is an area where Swift both
excels at (love what you do with types in the compiler) but also sometimes
lacking, as there seems to be a lot of idiosyncrasy, as evident from the
huge number of language keywords and compiler attributes. Here, I would
like to discuss some of the idiosyncrasy with closures. As I am trying to
throw some random ideas out there, my post might be a bit chaotic, so
please bear with me. Besides, I literally started playing around with
Swift only few hours ago (I know, I know, I’m late), so my practical
knowledge of the language is very limited.

1. I always wondered about the specific closure syntax { A -> B in … }.
The type annotation is within the block for some reason and the there is
the keyword ‘in’, which is usually associated with the for loop. My
proposal would be to change this to simply:

   A -> B { … }

That is, the closure declaration closely mirrors the function declaration,
but without the 'func name’ part. This is far from being just a cosmetic
change though. Rather, it has some far-reaching consequences to how the
concept of closure is applied to the language, but I believe that this
would make Swift much more streamlined and concise (and also solve some
other issues in the process).

The crucial point is that the type annotation is (and should be) optional.
Which means that every code block { … } is a closure. I think this
fundamentally makes a lot of sense. So, a function declaration is just a
closure associated with a name identifier (aka. injected into the
namespace). Similarly, code blocks used in statements such as for and do
have local lexical scope, which makes them essentially compatible closures
as far as I can see.

Essentially, this approach radically simplifies the language both from the
syntax standpoint — closures and funcs are using the same syntax, the
notion of code blocks is removed from the language; but also from the
semantics standpoint. For instance, one can give more formal definitions
for statements, which opens the path of treating statements as expressions
(see
https://lists.swift.org/pipermail/swift-evolution/2015-December/000133.html).
For instance, current if is of type (Bool, ()->())->(), but it can easily
be redeclared as (Bool, ()->T)->T, which opens way to nice things like

  let x = if(…) { .. some complex setup code..; value1} else { … other
code..; value2}

Similarly, do can be used to incapsulate auxiliary computations in the
local scope (see below).

2. Another closure limitation is that return type inference does not work
with longer closures (but they should). Here a particular use case. Due to
my work, I program a lot with R (which is sometimes a neat language in its
own right). A particular paradigm I find very useful is to use anonymous
code blocks in local scope to prevent namespace pollution, e.g. (in R code)

dataset <- local({
   a <- some_computation
   b <- some_computation

   a combined with b
})

// a and b are not visible in the outer scope anymore

In JavaScript, people often use anonymous functions for the same effect.
The nice thing about this paradigm is that it allows one to properly
isolate data dependency from each other, give the compiler a better sense
of variable lifespan and overall make your code much nicer to read. Of
course, in Swift this can be done with nested functions or closures, but
its the question of aesthetics. At any rate, the R function is very easy to
emulate in Swift:

func local<T>(closure: () -> T) -> T {
     return closure()
}

which works beautifully, e.g.

let (a1,a2) = local({return(1, 2)}

However, if I want to use more complex code, I need to explicitly declare
the closure type:

   let (a1, a2) = local({ () -> (Int, Int) in
     let x2 = x*2

     return (x2, x2+1)
   })

This is bulky, and frankly, unnecessary, as the compiler can easily infer
the return type of the closure.

BTW, combined with proposal in 1., this style of programming just becomes
the beautiful

let (a1, a2) = do {
  .. do something ..
return(…, …) // or simply (…, …)
}

Summary TL;DR version of the proposal:

replace the {A->B in …} closure syntax by A->B {…} — where type
declaration is optional, remove the concept of code block (replace it by
closure) and statements that take blocks/closures typed expressions.
Improve type inference for return values. Current syntactic sugar for
closures such as anonymous arguments and last line as return values should
be preserved.

Advantages: streamlines the language, solves a number of issues at the
same time (idiosyncrasy, operators as expressions). In the end, makes the
language easier to use and also to understand conceptually.

Disadvantages: I honestly can’t see a single one

Implementation challenge: would requires a parser change that might be
non-trivial. I can also imagine that a lot of refactoring in the compiler
code is necessary in regards to how statements work — but this might pay
off as in the end, the compiler only needs to deal with closures and calls,
which simplifies optimisation. And Swift is already great at aggressive
inlining of closures.

Impact on existing code: the syntax of closure declarations can be
automatically ported as the rewrite rule is trivial. As far as I can see,
changing code blocks to closures won’t have any impact on the semantics of
the control structures, because the blocks are already using lexical scope.

Eager to hear your thoughts on this!

Cheers,

Taras

_______________________________________________
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


(Taras Zakharko) #5

How would you write this with the new syntax?

I don't think the syntax { x in ... } is a hindrance for your proposal of turning all code blocks into closures, as all code blocks currently have no arguments and in that case both variants look the same. All code blocks with the exception of function bodies but I think having these looking different is ok.

Point well taken :slight_smile:

Other suggestions (out of top of my head) would be

// lambda-calculus inspired, but potentially confusing
someAsyncFunction() { error .
    // …
}

// like above, kind of constructor notation, my personal favourite so far
someAsyncFunction() { error |
    // …
}

// lest idiosyncratic but also verbose and ugly
someAsyncFunction() func(error) {
    // …
}

But you are right of course that the syntax I proposed in the original mail does not work with these examples. Again, its more of a cosmetic thing anyway. I think the other point (streamlining the concept of closures in the language) is more important.

Actually I was very surprised to see that your example with "local" returning a tuple did not work. The type inferencer should be able to infer the result type T to be a tuple without problems.

Yep, I was also quite surprised. But it does not work for me as of Xcode 7C68

Cheers,

Taras

P.S. Thorsten, sorry for wrong reply, I am not using mailing lists often and the behaviour of the mail client is a bit confusing.

···

On 13 Dec 2015, at 15:26, Marc Knaup <marc@knaup.koeln> wrote:
On 13 Dec 2015, at 09:42, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:
On 13 Dec 2015, at 09:42, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:

On 13 Dec 2015, at 15:26, Marc Knaup <marc@knaup.koeln> wrote:

How would you write this with the new syntax?

someAsyncFunction() { error in
    // …
}

This doesn't make sense (like Thorsten explained earlier):

someAsyncFunction() error {
    // …
}

I agree though that "in" is confusing and I never understood what "in" means here.
Since "do" is no longer used for loops but code blocks it would make sense to replace "in" with "do".

someAsyncFunction() { error do
    // …
}

On Sun, Dec 13, 2015 at 9:42 AM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Hi Taras,

I don't think the syntax { x in ... } is a hindrance for your proposal of turning all code blocks into closures, as all code blocks currently have no arguments and in that case both variants look the same. All code blocks with the exception of function bodies but I think having these looking different is ok.

An advantage of the current syntax is that you can leave off the parentheses when calling a closure if it is the last argument which already enables the syntax you would like for "local":

let x = local {
        ...
}

This requires having the arguments inside the braces to allow for things like these:

someCollection.forEach { x in ... }
File.open("some/path") { file in ... } // having the file closed automatically

Such usage patterns would look awkward with arguments outside of the braces and they are very common and very useful.

Actually I was very surprised to see that your example with "local" returning a tuple did not work. The type inferencer should be able to infer the result type T to be a tuple without problems.

-Thorsten

Am 13.12.2015 um 05:14 schrieb Taras Zakharko via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

Dear all,

first of all, thank you for giving the community the opportunity to influence the future of Swift. This is a strong move, which shows how passionate the Swift team is about their language and how you guys want to make it the best.

Personally, I am a huge fan of streamlined, logically concise concepts as well as syntax in programming languages. This is an area where Swift both excels at (love what you do with types in the compiler) but also sometimes lacking, as there seems to be a lot of idiosyncrasy, as evident from the huge number of language keywords and compiler attributes. Here, I would like to discuss some of the idiosyncrasy with closures. As I am trying to throw some random ideas out there, my post might be a bit chaotic, so please bear with me. Besides, I literally started playing around with Swift only few hours ago (I know, I know, I’m late), so my practical knowledge of the language is very limited.

1. I always wondered about the specific closure syntax { A -> B in … }. The type annotation is within the block for some reason and the there is the keyword ‘in’, which is usually associated with the for loop. My proposal would be to change this to simply:

   A -> B { … }

That is, the closure declaration closely mirrors the function declaration, but without the 'func name’ part. This is far from being just a cosmetic change though. Rather, it has some far-reaching consequences to how the concept of closure is applied to the language, but I believe that this would make Swift much more streamlined and concise (and also solve some other issues in the process).

The crucial point is that the type annotation is (and should be) optional. Which means that every code block { … } is a closure. I think this fundamentally makes a lot of sense. So, a function declaration is just a closure associated with a name identifier (aka. injected into the namespace). Similarly, code blocks used in statements such as for and do have local lexical scope, which makes them essentially compatible closures as far as I can see.

Essentially, this approach radically simplifies the language both from the syntax standpoint — closures and funcs are using the same syntax, the notion of code blocks is removed from the language; but also from the semantics standpoint. For instance, one can give more formal definitions for statements, which opens the path of treating statements as expressions (see https://lists.swift.org/pipermail/swift-evolution/2015-December/000133.html). For instance, current if is of type (Bool, ()->())->(), but it can easily be redeclared as (Bool, ()->T)->T, which opens way to nice things like

  let x = if(…) { .. some complex setup code..; value1} else { … other code..; value2}

Similarly, do can be used to incapsulate auxiliary computations in the local scope (see below).

2. Another closure limitation is that return type inference does not work with longer closures (but they should). Here a particular use case. Due to my work, I program a lot with R (which is sometimes a neat language in its own right). A particular paradigm I find very useful is to use anonymous code blocks in local scope to prevent namespace pollution, e.g. (in R code)

dataset <- local({
   a <- some_computation
   b <- some_computation

   a combined with b
})

// a and b are not visible in the outer scope anymore

In JavaScript, people often use anonymous functions for the same effect. The nice thing about this paradigm is that it allows one to properly isolate data dependency from each other, give the compiler a better sense of variable lifespan and overall make your code much nicer to read. Of course, in Swift this can be done with nested functions or closures, but its the question of aesthetics. At any rate, the R function is very easy to emulate in Swift:

func local<T>(closure: () -> T) -> T {
     return closure()
}

which works beautifully, e.g.

let (a1,a2) = local({return(1, 2)}

However, if I want to use more complex code, I need to explicitly declare the closure type:

   let (a1, a2) = local({ () -> (Int, Int) in
     let x2 = x*2

     return (x2, x2+1)
   })

This is bulky, and frankly, unnecessary, as the compiler can easily infer the return type of the closure.

BTW, combined with proposal in 1., this style of programming just becomes the beautiful

let (a1, a2) = do {
  .. do something ..
return(…, …) // or simply (…, …)
}

Summary TL;DR version of the proposal:

replace the {A->B in …} closure syntax by A->B {…} — where type declaration is optional, remove the concept of code block (replace it by closure) and statements that take blocks/closures typed expressions. Improve type inference for return values. Current syntactic sugar for closures such as anonymous arguments and last line as return values should be preserved.

Advantages: streamlines the language, solves a number of issues at the same time (idiosyncrasy, operators as expressions). In the end, makes the language easier to use and also to understand conceptually.

Disadvantages: I honestly can’t see a single one

Implementation challenge: would requires a parser change that might be non-trivial. I can also imagine that a lot of refactoring in the compiler code is necessary in regards to how statements work — but this might pay off as in the end, the compiler only needs to deal with closures and calls, which simplifies optimisation. And Swift is already great at aggressive inlining of closures.

Impact on existing code: the syntax of closure declarations can be automatically ported as the rewrite rule is trivial. As far as I can see, changing code blocks to closures won’t have any impact on the semantics of the control structures, because the blocks are already using lexical scope.

Eager to hear your thoughts on this!

Cheers,

Taras _______________________________________________
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

On 13 Dec 2015, at 15:26, Marc Knaup <marc@knaup.koeln> wrote:

How would you write this with the new syntax?

someAsyncFunction() { error in
    // …
}

This doesn't make sense (like Thorsten explained earlier):

someAsyncFunction() error {
    // …
}

I agree though that "in" is confusing and I never understood what "in" means here.
Since "do" is no longer used for loops but code blocks it would make sense to replace "in" with "do".

someAsyncFunction() { error do
    // …
}

On Sun, Dec 13, 2015 at 9:42 AM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Hi Taras,

I don't think the syntax { x in ... } is a hindrance for your proposal of turning all code blocks into closures, as all code blocks currently have no arguments and in that case both variants look the same. All code blocks with the exception of function bodies but I think having these looking different is ok.

An advantage of the current syntax is that you can leave off the parentheses when calling a closure if it is the last argument which already enables the syntax you would like for "local":

let x = local {
        ...
}

This requires having the arguments inside the braces to allow for things like these:

someCollection.forEach { x in ... }
File.open("some/path") { file in ... } // having the file closed automatically

Such usage patterns would look awkward with arguments outside of the braces and they are very common and very useful.

Actually I was very surprised to see that your example with "local" returning a tuple did not work. The type inferencer should be able to infer the result type T to be a tuple without problems.

-Thorsten

Am 13.12.2015 um 05:14 schrieb Taras Zakharko via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

Dear all,

first of all, thank you for giving the community the opportunity to influence the future of Swift. This is a strong move, which shows how passionate the Swift team is about their language and how you guys want to make it the best.

Personally, I am a huge fan of streamlined, logically concise concepts as well as syntax in programming languages. This is an area where Swift both excels at (love what you do with types in the compiler) but also sometimes lacking, as there seems to be a lot of idiosyncrasy, as evident from the huge number of language keywords and compiler attributes. Here, I would like to discuss some of the idiosyncrasy with closures. As I am trying to throw some random ideas out there, my post might be a bit chaotic, so please bear with me. Besides, I literally started playing around with Swift only few hours ago (I know, I know, I’m late), so my practical knowledge of the language is very limited.

1. I always wondered about the specific closure syntax { A -> B in … }. The type annotation is within the block for some reason and the there is the keyword ‘in’, which is usually associated with the for loop. My proposal would be to change this to simply:

   A -> B { … }

That is, the closure declaration closely mirrors the function declaration, but without the 'func name’ part. This is far from being just a cosmetic change though. Rather, it has some far-reaching consequences to how the concept of closure is applied to the language, but I believe that this would make Swift much more streamlined and concise (and also solve some other issues in the process).

The crucial point is that the type annotation is (and should be) optional. Which means that every code block { … } is a closure. I think this fundamentally makes a lot of sense. So, a function declaration is just a closure associated with a name identifier (aka. injected into the namespace). Similarly, code blocks used in statements such as for and do have local lexical scope, which makes them essentially compatible closures as far as I can see.

Essentially, this approach radically simplifies the language both from the syntax standpoint — closures and funcs are using the same syntax, the notion of code blocks is removed from the language; but also from the semantics standpoint. For instance, one can give more formal definitions for statements, which opens the path of treating statements as expressions (see https://lists.swift.org/pipermail/swift-evolution/2015-December/000133.html). For instance, current if is of type (Bool, ()->())->(), but it can easily be redeclared as (Bool, ()->T)->T, which opens way to nice things like

  let x = if(…) { .. some complex setup code..; value1} else { … other code..; value2}

Similarly, do can be used to incapsulate auxiliary computations in the local scope (see below).

2. Another closure limitation is that return type inference does not work with longer closures (but they should). Here a particular use case. Due to my work, I program a lot with R (which is sometimes a neat language in its own right). A particular paradigm I find very useful is to use anonymous code blocks in local scope to prevent namespace pollution, e.g. (in R code)

dataset <- local({
   a <- some_computation
   b <- some_computation

   a combined with b
})

// a and b are not visible in the outer scope anymore

In JavaScript, people often use anonymous functions for the same effect. The nice thing about this paradigm is that it allows one to properly isolate data dependency from each other, give the compiler a better sense of variable lifespan and overall make your code much nicer to read. Of course, in Swift this can be done with nested functions or closures, but its the question of aesthetics. At any rate, the R function is very easy to emulate in Swift:

func local<T>(closure: () -> T) -> T {
     return closure()
}

which works beautifully, e.g.

let (a1,a2) = local({return(1, 2)}

However, if I want to use more complex code, I need to explicitly declare the closure type:

   let (a1, a2) = local({ () -> (Int, Int) in
     let x2 = x*2

     return (x2, x2+1)
   })

This is bulky, and frankly, unnecessary, as the compiler can easily infer the return type of the closure.

BTW, combined with proposal in 1., this style of programming just becomes the beautiful

let (a1, a2) = do {
  .. do something ..
return(…, …) // or simply (…, …)
}

Summary TL;DR version of the proposal:

replace the {A->B in …} closure syntax by A->B {…} — where type declaration is optional, remove the concept of code block (replace it by closure) and statements that take blocks/closures typed expressions. Improve type inference for return values. Current syntactic sugar for closures such as anonymous arguments and last line as return values should be preserved.

Advantages: streamlines the language, solves a number of issues at the same time (idiosyncrasy, operators as expressions). In the end, makes the language easier to use and also to understand conceptually.

Disadvantages: I honestly can’t see a single one

Implementation challenge: would requires a parser change that might be non-trivial. I can also imagine that a lot of refactoring in the compiler code is necessary in regards to how statements work — but this might pay off as in the end, the compiler only needs to deal with closures and calls, which simplifies optimisation. And Swift is already great at aggressive inlining of closures.

Impact on existing code: the syntax of closure declarations can be automatically ported as the rewrite rule is trivial. As far as I can see, changing code blocks to closures won’t have any impact on the semantics of the control structures, because the blocks are already using lexical scope.

Eager to hear your thoughts on this!

Cheers,

Taras
_______________________________________________
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


(Thorsten Seitz) #6

Well, I read

{ error in … }

as meaning, „error“ is bound within („in“) the following statements. I’m fine with that.

-Thorsten

···

Am 13.12.2015 um 15:26 schrieb Marc Knaup <marc@knaup.koeln>:

I agree though that "in" is confusing and I never understood what "in" means here.
Since "do" is no longer used for loops but code blocks it would make sense to replace "in" with "do“.


(Marc Knaup) #7

Compare the following cases:

#1
for variable in sequence {}

#2
someFunction({ variable in sequence })

Case #1 assigns elements from sequence to variable, so sequence is the
SOURCE and variable is the DESTINATION.
Case #2 has variable as SOURCE and sequence as DESTINATION (return value).
It's flipped!
It feels like it's meaning the same ("destination IN source") but it is
not. That's inconsistent and confusing.

Changing #2 to the following breaks the inconsistency and avoids the
confusion:

someFunction({ variable do sequence })

I know the example doesn't make much sense as it just returns the sequence
and ignores the variable. But it's good enough to highlight the
inconsistency.

···

On Sun, Dec 13, 2015 at 3:47 PM, Thorsten Seitz <tseitz42@icloud.com> wrote:

Am 13.12.2015 um 15:26 schrieb Marc Knaup <marc@knaup.koeln>:

I agree though that "in" is confusing and I never understood what "in"
means here.
Since "do" is no longer used for loops but code blocks it would make sense
to replace "in" with "do“.

Well, I read

{ error *in* … }

as meaning, „error“ is bound within („in“) the following statements. I’m
fine with that.

-Thorsten


(Etan Kissling) #8

Problem is that without the `in` you could conflict with custom defined operators.

Right now, you can go with

[1, 2, 3, 4, 5].map {
    $0 + 1
}

Now assume a world where a user defined a custom postfix . or | operator.

var error = ""
someAsyncFunction() { error .
    // body
}

Is the "error" a named variable inside the closure, or the one used in the outer scope?

Is the . our custom postfix operator, or is it the delimiter between the argument list and the body?

With `in`, this problem cannot occur, as `in` cannot be used as an identifier.

Also don't forget about the whole mess when it starts with capture lists and explicit return type.

someAsyncFunction() { [capture list] (arg1: Arg1Type, arg2: Arg2Type, arg3: Arg3Type) -> ReturnType in
    // ...
}

Etan

···

On 13 Dec 2015, at 15:36, Taras Zakharko via swift-evolution <swift-evolution@swift.org> wrote:

On 13 Dec 2015, at 15:26, Marc Knaup <marc@knaup.koeln <mailto:marc@knaup.koeln>> wrote:

How would you write this with the new syntax?

On 13 Dec 2015, at 09:42, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:

I don't think the syntax { x in ... } is a hindrance for your proposal of turning all code blocks into closures, as all code blocks currently have no arguments and in that case both variants look the same. All code blocks with the exception of function bodies but I think having these looking different is ok.

Point well taken :slight_smile:

Other suggestions (out of top of my head) would be

// lambda-calculus inspired, but potentially confusing
someAsyncFunction() { error .
    // …
}

// like above, kind of constructor notation, my personal favourite so far
someAsyncFunction() { error |
    // …
}

// lest idiosyncratic but also verbose and ugly
someAsyncFunction() func(error) {
    // …
}

But you are right of course that the syntax I proposed in the original mail does not work with these examples. Again, its more of a cosmetic thing anyway. I think the other point (streamlining the concept of closures in the language) is more important.

On 13 Dec 2015, at 09:42, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:

Actually I was very surprised to see that your example with "local" returning a tuple did not work. The type inferencer should be able to infer the result type T to be a tuple without problems.

Yep, I was also quite surprised. But it does not work for me as of Xcode 7C68

Cheers,

Taras

P.S. Thorsten, sorry for wrong reply, I am not using mailing lists often and the behaviour of the mail client is a bit confusing.

On 13 Dec 2015, at 15:26, Marc Knaup <marc@knaup.koeln <mailto:marc@knaup.koeln>> wrote:

How would you write this with the new syntax?

someAsyncFunction() { error in
    // …
}

This doesn't make sense (like Thorsten explained earlier):

someAsyncFunction() error {
    // …
}

I agree though that "in" is confusing and I never understood what "in" means here.
Since "do" is no longer used for loops but code blocks it would make sense to replace "in" with "do".

someAsyncFunction() { error do
    // …
}

On Sun, Dec 13, 2015 at 9:42 AM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Hi Taras,

I don't think the syntax { x in ... } is a hindrance for your proposal of turning all code blocks into closures, as all code blocks currently have no arguments and in that case both variants look the same. All code blocks with the exception of function bodies but I think having these looking different is ok.

An advantage of the current syntax is that you can leave off the parentheses when calling a closure if it is the last argument which already enables the syntax you would like for "local":

let x = local {
        ...
}

This requires having the arguments inside the braces to allow for things like these:

someCollection.forEach { x in ... }
File.open("some/path") { file in ... } // having the file closed automatically

Such usage patterns would look awkward with arguments outside of the braces and they are very common and very useful.

Actually I was very surprised to see that your example with "local" returning a tuple did not work. The type inferencer should be able to infer the result type T to be a tuple without problems.

-Thorsten

Am 13.12.2015 um 05:14 schrieb Taras Zakharko via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

Dear all,

first of all, thank you for giving the community the opportunity to influence the future of Swift. This is a strong move, which shows how passionate the Swift team is about their language and how you guys want to make it the best.

Personally, I am a huge fan of streamlined, logically concise concepts as well as syntax in programming languages. This is an area where Swift both excels at (love what you do with types in the compiler) but also sometimes lacking, as there seems to be a lot of idiosyncrasy, as evident from the huge number of language keywords and compiler attributes. Here, I would like to discuss some of the idiosyncrasy with closures. As I am trying to throw some random ideas out there, my post might be a bit chaotic, so please bear with me. Besides, I literally started playing around with Swift only few hours ago (I know, I know, I’m late), so my practical knowledge of the language is very limited.

1. I always wondered about the specific closure syntax { A -> B in … }. The type annotation is within the block for some reason and the there is the keyword ‘in’, which is usually associated with the for loop. My proposal would be to change this to simply:

   A -> B { … }

That is, the closure declaration closely mirrors the function declaration, but without the 'func name’ part. This is far from being just a cosmetic change though. Rather, it has some far-reaching consequences to how the concept of closure is applied to the language, but I believe that this would make Swift much more streamlined and concise (and also solve some other issues in the process).

The crucial point is that the type annotation is (and should be) optional. Which means that every code block { … } is a closure. I think this fundamentally makes a lot of sense. So, a function declaration is just a closure associated with a name identifier (aka. injected into the namespace). Similarly, code blocks used in statements such as for and do have local lexical scope, which makes them essentially compatible closures as far as I can see.

Essentially, this approach radically simplifies the language both from the syntax standpoint — closures and funcs are using the same syntax, the notion of code blocks is removed from the language; but also from the semantics standpoint. For instance, one can give more formal definitions for statements, which opens the path of treating statements as expressions (see https://lists.swift.org/pipermail/swift-evolution/2015-December/000133.html). For instance, current if is of type (Bool, ()->())->(), but it can easily be redeclared as (Bool, ()->T)->T, which opens way to nice things like

  let x = if(…) { .. some complex setup code..; value1} else { … other code..; value2}

Similarly, do can be used to incapsulate auxiliary computations in the local scope (see below).

2. Another closure limitation is that return type inference does not work with longer closures (but they should). Here a particular use case. Due to my work, I program a lot with R (which is sometimes a neat language in its own right). A particular paradigm I find very useful is to use anonymous code blocks in local scope to prevent namespace pollution, e.g. (in R code)

dataset <- local({
   a <- some_computation
   b <- some_computation

   a combined with b
})

// a and b are not visible in the outer scope anymore

In JavaScript, people often use anonymous functions for the same effect. The nice thing about this paradigm is that it allows one to properly isolate data dependency from each other, give the compiler a better sense of variable lifespan and overall make your code much nicer to read. Of course, in Swift this can be done with nested functions or closures, but its the question of aesthetics. At any rate, the R function is very easy to emulate in Swift:

func local<T>(closure: () -> T) -> T {
     return closure()
}

which works beautifully, e.g.

let (a1,a2) = local({return(1, 2)}

However, if I want to use more complex code, I need to explicitly declare the closure type:

   let (a1, a2) = local({ () -> (Int, Int) in
     let x2 = x*2

     return (x2, x2+1)
   })

This is bulky, and frankly, unnecessary, as the compiler can easily infer the return type of the closure.

BTW, combined with proposal in 1., this style of programming just becomes the beautiful

let (a1, a2) = do {
  .. do something ..
return(…, …) // or simply (…, …)
}

Summary TL;DR version of the proposal:

replace the {A->B in …} closure syntax by A->B {…} — where type declaration is optional, remove the concept of code block (replace it by closure) and statements that take blocks/closures typed expressions. Improve type inference for return values. Current syntactic sugar for closures such as anonymous arguments and last line as return values should be preserved.

Advantages: streamlines the language, solves a number of issues at the same time (idiosyncrasy, operators as expressions). In the end, makes the language easier to use and also to understand conceptually.

Disadvantages: I honestly can’t see a single one

Implementation challenge: would requires a parser change that might be non-trivial. I can also imagine that a lot of refactoring in the compiler code is necessary in regards to how statements work — but this might pay off as in the end, the compiler only needs to deal with closures and calls, which simplifies optimisation. And Swift is already great at aggressive inlining of closures.

Impact on existing code: the syntax of closure declarations can be automatically ported as the rewrite rule is trivial. As far as I can see, changing code blocks to closures won’t have any impact on the semantics of the control structures, because the blocks are already using lexical scope.

Eager to hear your thoughts on this!

Cheers,

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

<open.gif>

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

On 13 Dec 2015, at 15:26, Marc Knaup <marc@knaup.koeln <mailto:marc@knaup.koeln>> wrote:

How would you write this with the new syntax?

someAsyncFunction() { error in
    // …
}

This doesn't make sense (like Thorsten explained earlier):

someAsyncFunction() error {
    // …
}

I agree though that "in" is confusing and I never understood what "in" means here.
Since "do" is no longer used for loops but code blocks it would make sense to replace "in" with "do".

someAsyncFunction() { error do
    // …
}

On Sun, Dec 13, 2015 at 9:42 AM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Hi Taras,

I don't think the syntax { x in ... } is a hindrance for your proposal of turning all code blocks into closures, as all code blocks currently have no arguments and in that case both variants look the same. All code blocks with the exception of function bodies but I think having these looking different is ok.

An advantage of the current syntax is that you can leave off the parentheses when calling a closure if it is the last argument which already enables the syntax you would like for "local":

let x = local {
        ...
}

This requires having the arguments inside the braces to allow for things like these:

someCollection.forEach { x in ... }
File.open("some/path") { file in ... } // having the file closed automatically

Such usage patterns would look awkward with arguments outside of the braces and they are very common and very useful.

Actually I was very surprised to see that your example with "local" returning a tuple did not work. The type inferencer should be able to infer the result type T to be a tuple without problems.

-Thorsten

Am 13.12.2015 um 05:14 schrieb Taras Zakharko via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

Dear all,

first of all, thank you for giving the community the opportunity to influence the future of Swift. This is a strong move, which shows how passionate the Swift team is about their language and how you guys want to make it the best.

Personally, I am a huge fan of streamlined, logically concise concepts as well as syntax in programming languages. This is an area where Swift both excels at (love what you do with types in the compiler) but also sometimes lacking, as there seems to be a lot of idiosyncrasy, as evident from the huge number of language keywords and compiler attributes. Here, I would like to discuss some of the idiosyncrasy with closures. As I am trying to throw some random ideas out there, my post might be a bit chaotic, so please bear with me. Besides, I literally started playing around with Swift only few hours ago (I know, I know, I’m late), so my practical knowledge of the language is very limited.

1. I always wondered about the specific closure syntax { A -> B in … }. The type annotation is within the block for some reason and the there is the keyword ‘in’, which is usually associated with the for loop. My proposal would be to change this to simply:

   A -> B { … }

That is, the closure declaration closely mirrors the function declaration, but without the 'func name’ part. This is far from being just a cosmetic change though. Rather, it has some far-reaching consequences to how the concept of closure is applied to the language, but I believe that this would make Swift much more streamlined and concise (and also solve some other issues in the process).

The crucial point is that the type annotation is (and should be) optional. Which means that every code block { … } is a closure. I think this fundamentally makes a lot of sense. So, a function declaration is just a closure associated with a name identifier (aka. injected into the namespace). Similarly, code blocks used in statements such as for and do have local lexical scope, which makes them essentially compatible closures as far as I can see.

Essentially, this approach radically simplifies the language both from the syntax standpoint — closures and funcs are using the same syntax, the notion of code blocks is removed from the language; but also from the semantics standpoint. For instance, one can give more formal definitions for statements, which opens the path of treating statements as expressions (see https://lists.swift.org/pipermail/swift-evolution/2015-December/000133.html). For instance, current if is of type (Bool, ()->())->(), but it can easily be redeclared as (Bool, ()->T)->T, which opens way to nice things like

  let x = if(…) { .. some complex setup code..; value1} else { … other code..; value2}

Similarly, do can be used to incapsulate auxiliary computations in the local scope (see below).

2. Another closure limitation is that return type inference does not work with longer closures (but they should). Here a particular use case. Due to my work, I program a lot with R (which is sometimes a neat language in its own right). A particular paradigm I find very useful is to use anonymous code blocks in local scope to prevent namespace pollution, e.g. (in R code)

dataset <- local({
   a <- some_computation
   b <- some_computation

   a combined with b
})

// a and b are not visible in the outer scope anymore

In JavaScript, people often use anonymous functions for the same effect. The nice thing about this paradigm is that it allows one to properly isolate data dependency from each other, give the compiler a better sense of variable lifespan and overall make your code much nicer to read. Of course, in Swift this can be done with nested functions or closures, but its the question of aesthetics. At any rate, the R function is very easy to emulate in Swift:

func local<T>(closure: () -> T) -> T {
     return closure()
}

which works beautifully, e.g.

let (a1,a2) = local({return(1, 2)}

However, if I want to use more complex code, I need to explicitly declare the closure type:

   let (a1, a2) = local({ () -> (Int, Int) in
     let x2 = x*2

     return (x2, x2+1)
   })

This is bulky, and frankly, unnecessary, as the compiler can easily infer the return type of the closure.

BTW, combined with proposal in 1., this style of programming just becomes the beautiful

let (a1, a2) = do {
  .. do something ..
return(…, …) // or simply (…, …)
}

Summary TL;DR version of the proposal:

replace the {A->B in …} closure syntax by A->B {…} — where type declaration is optional, remove the concept of code block (replace it by closure) and statements that take blocks/closures typed expressions. Improve type inference for return values. Current syntactic sugar for closures such as anonymous arguments and last line as return values should be preserved.

Advantages: streamlines the language, solves a number of issues at the same time (idiosyncrasy, operators as expressions). In the end, makes the language easier to use and also to understand conceptually.

Disadvantages: I honestly can’t see a single one

Implementation challenge: would requires a parser change that might be non-trivial. I can also imagine that a lot of refactoring in the compiler code is necessary in regards to how statements work — but this might pay off as in the end, the compiler only needs to deal with closures and calls, which simplifies optimisation. And Swift is already great at aggressive inlining of closures.

Impact on existing code: the syntax of closure declarations can be automatically ported as the rewrite rule is trivial. As far as I can see, changing code blocks to closures won’t have any impact on the semantics of the control structures, because the blocks are already using lexical scope.

Eager to hear your thoughts on this!

Cheers,

Taras
_______________________________________________
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


(Andrew Brown) #9

I've never really been able to read this naturally.
What opinions do people have on
{ with variable do sequence }
Or
with variable { sequences }

ABR.

···

On 13 Dec 2015, at 15:03, Marc Knaup via swift-evolution <swift-evolution@swift.org> wrote:

Compare the following cases:

#1
for variable in sequence {}

#2
someFunction({ variable in sequence })

Case #1 assigns elements from sequence to variable, so sequence is the SOURCE and variable is the DESTINATION.
Case #2 has variable as SOURCE and sequence as DESTINATION (return value).
It's flipped!
It feels like it's meaning the same ("destination IN source") but it is not. That's inconsistent and confusing.

Changing #2 to the following breaks the inconsistency and avoids the confusion:

someFunction({ variable do sequence })

I know the example doesn't make much sense as it just returns the sequence and ignores the variable. But it's good enough to highlight the inconsistency.

On Sun, Dec 13, 2015 at 3:47 PM, Thorsten Seitz <tseitz42@icloud.com> wrote:

Am 13.12.2015 um 15:26 schrieb Marc Knaup <marc@knaup.koeln>:

I agree though that "in" is confusing and I never understood what "in" means here.
Since "do" is no longer used for loops but code blocks it would make sense to replace "in" with "do“.

Well, I read

{ error in … }

as meaning, „error“ is bound within („in“) the following statements. I’m fine with that.

-Thorsten

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


(Brent Royal-Gordon) #10

Now assume a world where a user defined a custom postfix . or | operator.

“.” cannot be a custom operator; it’s reserved by the language. (But I think it’s way too easily missed to play such an important role.)

···

--
Brent Royal-Gordon
Architechies