Why can't closures have labels and be more readable?

I'm a C++/Obj-C programmer finding it ludicrously hard to switch to Swift.

I find that the constant ability (leading to very poor programmer code) to reduce verbosity, grammar and syntax and add tokens reduces readability and nowhere is this more apparent that with closures.

I'm working through (to my shame ;) ) Ray Wenderlich's Swift course and the closure chapter has this:

PS I loathe K&R as much as I do Swift so it's all in Allman formatting for clarity.

let handler: (Int, Int) -> Int =
{
    (a: Int, b: Int) -> Int in 
    
    // do Something else
    
    return someInt
}

Why oh why isn't this more simply and elegantly written as:

let handler = (a: Int, b: Int) -> Int
{
    // do Something else
    
    return someInt
}

The equals sign shows clearly that it's a closure definition assignment, as does the starting 'let'. But this way, all of the excesses, like the 'in' keyword, the repetition of the params / return type only this time with useful labels and additional tokens are removed and it looks and reads much more like a regular function and certainly a lot more clearly.

So, when you have a closure that has a lot of stuff going on and you can't reduce it to obfuscated minimalism ($ tokens), then why isn't is formatted and syntactically better like the suggestion above?

6 Likes

Without getting into the particulars, I would point out that ObjC closures have a much worse syntax, and no named parameters, either.

5 Likes

You can write something very close to what you want by allowing type inference to do its work:

let multiply =
{
    (a: Int, b: Int) -> Int in 
    
    // do Something else
    
    return a * b
}

The authors of the tutorial are writing the full type to make it clear what it’s doing, but you wouldn’t see that in most production Swift code.

9 Likes

The example in the tutorial is not taking advantage of type inference. The same closure can be written much more concisely, without relying on anonymous $ arguments.

Either

let multiply: (Int, Int) -> Int = { a, b in
    // do something else
    
    return a * b
}

or:

let multiply = { (a: Int, b: Int) -> Int in
    // do something else
    
    return a * b
}
8 Likes

You're not the only one who thinks Swift's closure syntax is complicated. But so also is Objective-C's. IMO I think the beauty of Swift's closure syntax is that it lets the programmer (in most cases) choose the level of explicit typing they have to provide. If you're someone who loves $, you're free to use the most terse closures. And on the other hand, if you really just cannot stand type inference for some peculiar reason, go ahead and slap types on all the things.

2 Likes

The cleanest way of writing this is to just make a local func declaration.

8 Likes

The closure syntax tries to serve a lot of different purposes; I tried to explain some of the historical context in a response to this thread. For your use case, Swift also supports nested named function declarations:

func multiplyByTwoAndThree(a: Int) -> (Int, Int) {
  func multiply(b: Int) -> Int {
    return a * b
  }
  return (multiply(b: 2), multiply(b: 3))
}

and these have all the powers of closures—they can refer to their outer context, and be passed around as function values—but also allow you to use the same syntax as top-level functions and methods, use argument labels, and so on.

As for why function types don't themselves support argument labels, see SE-111 for background; it was a cleanup of the type system and language design around function types and argument labels, to make it so that argument labels are consistently always a part of declaration names like they are in Objective-C. In accepting that proposal, we left the door open to considering allowing labels to appear again as part of declarations involving closures; in the future, we could again allow var multiply: (a: Int, b: Int) -> Int as a declaration of multiply(a:b:) as a variable.

10 Likes

I think somewhere in some other thread someone mentioned that this was already planned? Would be really awesome to get the labels back in there :slight_smile:

4 Likes

We can and will eventually improve this by allowing compound names and with some extra sugar on top it will become what you want, at least from the call side, this won‘t add argument labels even if it seems that it should because you have parameter labels which are faked.

3 Likes

HI, Sorry but you’ve missed the point. It’s not about the pseudo code, it’s about closure definitions and syntax.

Please read the post entirely.

I agree, Obj-C’s implementation is horrible too, and other than callback handlers that call other methods (for clarity) I refuse to use them as they reduce code readability and clarity.

I’ve had team members bury so much functionality hidden in (reduced syntax) closures everywhere the code is obfuscated and a rats tail nest to unpack. And these guys think it’s clever or efficient... /smh

But back to this point, having the Params and return type as part of the declaration and not buried or repeated in the body of the closure is more in keeping with all other function declarations, reduces typing and massively increases clarity.

The existing implementation is horrific.

And again, I’ll repeat, the pseudo code could be boiled to one line, but my point is that the closure does more than one line and couldn’t be boiled down - I didn’t see the point of typing extra stuff to make the point but people aren’t reading it properly.

2 Likes

You’re right, they are awful too.

1 Like

The things you've said you want to do — define a closure in a highly-readable style, including things like parameter names — are well-suited to a local func. The anonymous-closure syntax is optimized for "minimalism" and convenience precisely because func is available as a less compact and more readable alternative.

10 Likes

Also, flaming is not welcome on these forums. You're free to criticize Swift's design and ask for changes, but you can do better than calling it stupid over and over again. As a moderator, I try to be very lenient with how people phrase their criticisms of the language (and/or the core team), but there have to be limits or else that kind of posting will drive everyone else away.

14 Likes

Since when did 'minimalism' creep in to Swift as an edict? Apple clearly still says that code should be readable over brevity, and it should read like English.

How does anyone justify the mess of Swift's ability to reduce code to symbols and tokens and claim it's readable?

How does having (at least) three different ways to define a closure help with readability over brevity?

Is this something that crept in after it went open source or was it something from Apple?

I genuinely want to know?

If all (at least) 3 ways of defining a closure are correct, why do you need 3? Why not just have the most clear (which as I've said, isn't really clear and is very poor syntactically) and limit it to one?

This isn't the 70's. Our screens are big enough to show methods of a decent size, and if a method goes over a screen of a 27" iMac, you're doing it wrong. We need fast, scan-able, readable code - not brevity for brevities sake.

Same applies to 1TBS, as Erica Sadun and many others will state - you can't scan or parse code as quickly or easily when people use 1TBS as opposed to Allman.

The days of 'saving space' and writing 'compact code' are dead. We should let the compiler optimise it far better than virtually every programmer out there, and just write code everyone can easily read.

1 Like

Are you interested in listening to anything anybody is saying, or are you just venting?

2 Likes

I asked a question. I said I genuinely want to know.

Do you know the answer?

And btw, I have read everything people have said on this thread; and the takeaway from the people that understood I wasn't talking about a closure that could be reduced to just tokens, was that the 2nd response using inference, was the closest we could get to my suggestion.

And yes, it's a little cleaner, I still don't understand why you need the params inside the closure and the IMHO silly token of 'in' - as I demonstrated in my suggestion.

I'm not saying my suggestion is perfect, but I've yet to read any response as to why it's not better than the awkward reading mess we have.

I'm here and willing to be educated, but so far, there's no reasonable argument been made for the complexity and just unpleasantness of closure syntax.

Minimalism is not an “edict.” Swift has a range of syntaxes for anonymous closures because the optimal amount of information to include in a closure is situational: very short closures benefit from not needing an explicit parameter list at all, while longer closures benefit from being able to name parameters and (occasionally) give them types.

There’s never a reason to write what you’ve written in your first post, though, and since that’s your only example of how terrible Swift syntax is, it’s not surprising that people keep responding to it.

Swift’s closure syntax predates the language being open-source by several years.

1 Like

In particular, since you are giving the closure a name, you should use a nested function, which completely eliminates the syntax that you find objectionable.

2 Likes