Allow function definitions to omit parentheses if no parameters

Who's asking for that? Foo.bar references the method. That would be a source and ABI incompatible change.

Let me summarize since we're now just going around and around on this and I feel we're losing the tenor of a productive discussion:

You have not demonstrated to me, and to a few others in this thread, that the shorthand you're defending has semantic value.

Because it is absolutely possible to remove things and wind up with a more complex result at the end of the day. Saving characters absolutely has nothing to do with saving head space without a compelling justification.

6 Likes

Isn't the point of the proposal that the longhand has no semantic value?

The counterpoint would be that either it does have semantic value, or that it's meaningfully different to the numerous other places where the language has allowed removal of redundant syntax.

What is the argument there?

Omitting returns in single expression functions/closures had a seemingly identical reasoning. What makes this different?

I see some mixed info in this thread and just as a reminder wanna say that at this time I am only proposing that parentheses be optional when defining a function that has no parameters. When the function is called, it is called with the function call operator ‘()’ just as it is now.

About what I mentioned earlier about computed properties with void type:

They should still be allowed but personally since they should be O(1) complexity and not do any heavy lifting the only thing I could imagine they could do is return void.

But this proposal as it is now does not address that, it only addresses that this:

func flush() { }

Should be optionally allowed to be defined as this:

func flush { }

When this is called it would be called no different than before, with the function call operator ‘()’:

flush()

Or as a method:

foo.flush()

flush would refer to the function pointer after definition, flush() would call it same as it is now.

@John_McCall Thank you for giving me feedback and the heads up but why would this change be seen as so negative?

I hoped it would be a tiny +1 win for Swift developers like trailing closure syntax.

If a developer does not want to use trailing closure syntax they can simply not use it, if they want to use it they can use the shorthand.

1 Like

All you've done is propose post-hoc rationalizations for why the current shortcuts are acceptable to you. You have not presented any reasoning as to why we should not be able to omit parentheses when defining a function. You certainly have failed in the face of being able to omit them when calling the function.

I am not arguing for this proposal. I am just pointing out that you don't have a valid argument against.

1 Like

I'd like to ask more explicitly: why is this considered acceptable?

func f(block: () -> ()) { ... }

f { }

While this is not?

func f { }

f()

The former allows the parentheses to be omitted at the point of use, because they would be extraneous noise. The latter allows them to be omitted at the point of declaration, because they are extraneous noise.

3 Likes

I think this is the exact point people are discussing here. Are those parenthesis noise or not?

I'll attempt not at expressing my opinion, but I'll make an hypothesis as why @John_McCall above says it is "unlikely that this proposal would be accepted":

Here is how I think it goes: functions should always be defined, and called, with parenthesis. This is what distinguishes functions from other type members like properties or subscripts. Or this is what it means to belong to the C-family of languages. Either way, those parenthesis are not noise. There is no exception.

...But the trailing closure. The trailing closure comes from the early ages of Swift, at a time Smalltalk or Ruby were active influences. It was desirable to open the door for methods that look like control structures, look like extensions of the language. Eventually, we don't have Bool.if(_:else:), but it is safe to bet it has been discussed. We can today see the effect of this very early design decision in constructs like dispatchQueue.async { ... }.

The elision of the parenthesis in trailing closures is not a matter of removing noise, because there was no noise to remove in the first place. This elision is a matter of opening the door to Smalltalk-like constructs.

5 Likes

Your presentation is the best reasoning against that I've read in this thread.

And it's not mine ;-) It's a picture I made of what may exist in the head(s) of the core team ;-)

Very good point!

then -> Void at the end of a function that returns void should be mandatory as well by that logic if void inputs () should be explicitly written then Void return types should not be allowed to be optionally omitted but I don't think anyone would welcome that change.

I won't pretend I pictured a fully consistent reasoning without any hole. There are subjective opinions at play, and a great deal of good taste, in the design area we are discussing. We can ask for good faith, but not for full consistency.

Your argument leads to the conclusion that Void functions should also require a return () somewhere in the body. The reason no one thinks so is because the common mental model of a Void return type is that the function doesn't return a value at all (even though this is technically not true in Swift). To accommodate this model, Swift allows eliding -> Void from the definition.

However, the reverse is not true. The parentheses after the function name do not indicate that the function takes no parameters. Rather, they indicate that it's a function to begin with. That there is nothing (but whitespace) between them is what indicates that it takes no parameters.

To put it another way: parentheses indicate function; emptiness indicates lack of parameters.

5 Likes

Imagine SwiftUI without function trailing closure syntax?

IMO, It would be very unsightly.

The thing that bothers me is the lack of symmetry here:

func foo() { ... } omitting the return type -> Void here is currently ok.

func foo { } omitting the return type is ok in this case but omitting the parameter list is not, this is currently an error.

but for some reason parameters are second class citizens to return types,
they don't get the same option to be omitted that return types do.

I would, but I am unlikely to get it :) (still mourn for the argument labels in closures...).

I thought func was enough to indicate that it is a function.

3 Likes

I think so, too. I am quite familiar with (scripting) languages which have no formal parameter lists for functions. I am quite comfortable with omitting (empty) parameter lists in function definitions.

My goal in this thread is to ensure that arguments for and against actually rebut the points made by either side. I started off against the pitch, but now I am neutral.

1 Like

Thanks! Yeah we are all on the same team, everyone here who is commenting wants to see Swift evolve to its next stage of evolution. Its really cool having this discussion!

We all only want whats best for the language but what is "best" its a mix of raw logic and taste.

I love things like trailing closure function syntax and the fact that now we can omit return with computed properties, just trying to see if we could add this change to that growing list of things that make programming in Swift fun for everyone.

This married with implicit single line returns makes a lot of sense.

1 Like

Thank you for laying out a good argument! :slightly_smiling_face: Consistent spelling of basic Types does seem important. There has to be a line drawn somewhere, and that may be the point that currently makes the most sense.

Side-note: I find it interesting that Objective-C has the opposite constraints. A return Type is always required, but no parameters require no syntax (one of few things I like when in Objective-C land).

This is kind off topic, but I want to use it as an example:

Switching between Swift and Kotlin has me wanting the option to remove some of Swift's syntax when it seems unnecessary. Kotlin largely accomplishes this by making everything an expression (which has it's own frustrations but that's a different issue). I think there would be ways to enable those types of ergonomics in Swift without Kotlin's (likely off the table) approach of making everything an Expression.

e.g. doing something like this:

//Kotlin:
fun makeFullName() = "$firstName $lastName"

//Swift maybe someday?
func makeFullname -> "\(firstName) \(lastName)"

To those used to C-based languages that code probably looks wrong as it did to me when I first started seeing it. And yes, it can certainly be abused, however it also simplifies other things in ways that I've come to value and rely on. E.g. with coroutines, and concurrency in general:

fun refreshData() = viewModelScope.launch {
    // you're now in a coroutine that's aware of this viewModel's attached lifecycle.
}

fun updateUI() = runInUIThread {
    //You're now safe to update the UI.
}

// Maybe Swift someday?
func updateUI -> DispatchQueue.main.async {
    //do your updates.
}

Not that I'm not pitching this. My more general point is syntax flexibility is very powerful. Yes, it can be abused, and it may also enable awesome features that haven't even been thought of yet.

Restrictions come at a cost. If making empty parameter lists optional would cause spelling, implementation, or clarity issues it's probably worth the constraints. However if the reason is consistency for it's own sake or that people currently think it looks funny, IMO it's at least worth considering whether the sacrifice in flexibility is worth it.

4 Likes

I came back to Xcode this week after three weeks of holidays, and started writing func something(), noted that I didn't have to write -> (), and wondered if I couldn't also elide the parentheses after the function name. It just felt "swifty" to me that day, as I had already specified that I was writing a func.

So I feel this pitch is not just frivolous. What speaks against it would be if it makes parsing or tooling more difficult, or if it causes extra debate at every Swift project where at least one person has very explicit opinions about code style, leading to this thread being repeated at every larger Swift site and linters being used in anger.

2 Likes