[swift-evolution] Closure Syntax

Ethan Diamond edgewood7558 at gmail.com
Tue Dec 29 18:18:03 CST 2015


I guess the core of the question for me is why have the concept of a
closure at all when they're harder to read while acting the same way that
functions do? I'm not a huge fan of Javascript, but the consistancy between
function declarations and anonymous functions is something I feel they got
right. JS syntax for everything that receives parameters and possibly
returns a value is entirely consistent.

Let's take the anonymous function style:

func asyncWithCallback(_ : func (String) -> Bool)

asyncWithCallback(func (param) {
    return param == "string"
})

Particularly for someone new to the language is both clearer and shorter
than this, which makes the caller rewrite the return type in the closure:

func asyncWithCallback(_ : String -> Bool)

asyncWithCallback {
  (param: String) -> Bool in
  return param == "string"
}

It still fits your unwritten rule to be able to compose language features
in Swift, assuming you leave the same syntactic sugar:

func if (_ control: Bool, _ path: func ()) {

  if (control) {

     path()

  }

}

if (a == b) {

  //LETS DO SOME STUFF

}

I know it's a big change to the language, but I feel like it's better
in just about every case I can think of without losing the Swiftiness
we all enjoy. It certainly makes this easier to read. Even in one of
the examples in the Swift guide
(https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html)
you're essentially using the syntax i'm proposing on the incrementer:

func makeIncrementer(forIncrement amount: Int) -> () -> Int {

    var runningTotal = 0

    func incrementer() -> Int {

        runningTotal += amount

        return runningTotal

    }

    return incrementer

}

I can't really fix the unclear chained return in the declaration, but
doesn't the return of the anonymous function look similar to the
above, just as you would expect?

func makeIncrementer(forIncrement amount: Int) -> func() -> Int {

   var runningTotal = 0

   return func () -> Int {

    runningTotal += amount

    return runningTotal

  }

}

But if I wanted to use closure syntax, It'd be different from the
example above in the Swift handbook for no real good reason:

func makeIncrementer(forIncrement amount: Int) -> () -> Int {

    var runningTotal = 0

    return {

        () -> Int in

        runningTotal += amount

        return runningTotal

    }

}

In the commonly rejected list, Jordan wrote:

"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 '->'."

I don't believe that's a problem when using the proposed func syntax,
since by definition "->" will only follow a func (param) statement in
all cases. The compiler can make more assumptions about what it's
parsing. I think it might limit what you have to work with to the
compilers benefit.

Thanks,

- Ethan


On Mon, Dec 28, 2015 at 5:44 PM, Chris Lattner <clattner at apple.com> wrote:

>
> On Dec 27, 2015, at 2:30 PM, Ethan Diamond via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> I realize this is on the commonly rejected list, but I really find closure
> syntax to be one of the more unintuitive and unreadable parts of Swift so
> I'd like to try to influence it. "Clarity at the point of use" is one of
> the main stated goals of Swift, and the fact that
> http://goshdarnclosuresyntax.com/ exists shows me that it's not just me
> that thinks the syntax could be improved. I believe that we can get very
> close to the same results in line length and expressiveness while making
> the language much more readable and clear.
>
>
> FWIW, I think that web site exists as a continuation of the “blocks” web
> site.
>
> From your description, it sounds like you might want to try out nested
> functions.  They have a more explicit syntax, and still provide the same
> “closure” power as closure expressions.
>
> -Chris
>
>
> Let's start with a few use cases to illustrate what I mean when I say that
> reading closure syntax is difficult. Most programmers scan left to right,
> so when I see this property declaration:
>
> var thing: Int
>
> My brain reads that as a property of a class that's an integer. Unless of
> course there's this:
>
> var thing: Int -> Int
>
> Boom, context switch. If you've read "Int" than any following syntax
> should be a modifier on Int. For example, Int? works great, because in my
> head it's still an Integer, just an optional form of that Integer. While
> it's not a huge change in that example, lets take a more complicated one:
>
> var thing: (String -> (), Int, (Int, Int) -> Bool)) -> Bool
>
> Reading that left to right requires all sorts of context switching in my
> brain. I can't even tell at first glance how many params are in that
> closure. Reading left to right, you read "First parameter, string, no wait,
> closure that takes string, and returns void. Second param, Int. Third
> param, tuple with two ints, no wait, closure that takes two ints and
> returns bool." I just doesn't have much clarity.
>
> I believe it's already been proposed, but I don't feel there's a strong
> enough difference between a closure and a function to justify a different
> syntax. Let's replace my examples above with anonymous function syntax.
>
> var thing: func (Int) -> Int
>
> Reading left to right, it reads the way that I think about it "A function
> that takes an integer and returns an integer."
>
> var thing: func(func (String), Int, func (Int, Int) -> Bool) -> Bool
>
> Again, reading left to right this is a win. "Thing is an anonymous
> function. First param, a function that takes a string. Second param, Int.
> Third param, a function that takes two ints and returns bool." It reads
> like people think.
>
> Another strength is it lets us both use the same syntax for closures as we
> would expect, while letting us use just about all of the same shorthands we
> gain with closures. We could call normally like this, with the return type
> implied when async is called:
>
> func async(callback: func (Bool) -> Bool))
>
> async(func (successful: Bool) {
>    return !successful
> });
>
> We could still permit this:
>
> func async(callback: func ())
>
> async {
>   //Do callback stuff here
> }
>
> We could still permit this:
>
> func sort(sortfunc: func(Int, Int) -> Bool)
>
> sort {
>   $0 > $1
> }
>
> We could add this:
>
> let greaterThan: func (number: Int) -> Bool = {
>    number > 5
> }
>
> There would also be a few small wins, such as no longer needing "()" to
> represent a void return, since any function without a return type would
> imply a void return.
>
> I understand that a big part of the decision for current closure syntax is
> to help the compiler, but I believe by doing so you're going against the
> main principles you laid out in your api design guidelines (
> https://swift.org/documentation/api-design-guidelines.html). Current
> closure syntax is not clear and breaks precedent of all other function like
> declarations having the parameters listed outside of the curly braces.
>
> Thanks for listening, and great job on Swift so far.
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151229/45e234c8/attachment.html>


More information about the swift-evolution mailing list