2

In Swift, functions can be defined with named parameters, for example:

func greet(person: String) -> String { ... } 

which need to be used when the function is called :

greet(person: "Anna") 

But it's not allowed to pass named parameters to closures.

What is the reason for such difference between named functions and closures in the language design?

10
  • 1
    Questions like this one tend to get closed as being opinion-based. Can you edit your question to include the research you've done? Can you describe a situation where named parameters in a closure would have solved a problem? Commented Oct 28, 2024 at 12:14
  • 3
    In Swift, named parameters are not part of a function type, but are part of the function name. The functions foo(_:_:) and bar(_:param:) both take 2 params and might have the same type. Closures do not have a name, they are anonymous functions. You cannot declare a variable with labels (e.g. let myClosure(x:y:) = {...}. Parameter labels get erased when using a function or closure as a value. This changed in Swift 3, see github.com/swiftlang/swift-evolution/blob/main/proposals/… Commented Oct 28, 2024 at 13:46
  • 2
    @amon This is exactly the kind of non-opinion based answer a curious reader would expect. I vote to reopen the question so that you can post it. Commented Oct 29, 2024 at 8:28
  • 3
    @GregBurghardt I don't understand why this is opinion based. The designers of Swift had a reason to design their language like that. I'm interested in that reason. Commented Oct 29, 2024 at 10:01
  • 2
    @DarkTrick see Is asking "why" on language specifications still considered as "primarily opinion-based" if it can have official answers? Commented Oct 29, 2024 at 11:20

1 Answer 1

4

This was decided in SE-0111, Remove type system significance of function argument labels, which cites the following motivation:

Motivation

...

As currently implemented, this feature can lead to surprising behavior:

func sinkBattleship(atX x: Int, y: Int) -> Bool { /* ... */ } func meetsBattingAverage(ofHits hits: Int, forRuns runs: Int) -> Bool { /* ... */ } var battingAveragePredicate : (ofHits: Int, forRuns: Int) -> Bool = meetsBattingAverage battingAveragePredicate = sinkBattleship // sinkBattleship is invoked battingAveragePredicate(ofHits: 1, forRuns: 2) 

Removing this feature simplifies the type system.

In other words, the argument labels were in a strange limbo, where they were significant enough to impact the inferred type of variables (e.g. battingAveragePredicate infers as (ofHits: Int, forRuns: Int) -> Bool), but were also loose enough that sinkBattleship could be assigned, despite having different argument labels.

The result is a call where the labels are (ofHits: 1, forRuns: 2), but actually behave as a call to (atX: 1, y: 2). This was confusing, and just got torn out entirely.

2
  • @Craig Fixed, thanks! I've run into that min limit before, I find it quite annoying haha Commented Oct 30, 2024 at 14:13
  • Your fix is better, I was going to put it in the wrong place. :) Commented Oct 30, 2024 at 14:19

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.