[swift-evolution] Streamlining closures

Taras Zakharko taras.zakharko at uzh.ch
Sat Dec 12 22:14:42 CST 2015


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 <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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151213/d7b1fbc3/attachment.html>


More information about the swift-evolution mailing list