Allow function definitions to omit parentheses if no parameters

And I think stylistic choices should be rationalized with something stronger than "it exists elsewhere" and subjective arguments that the syntax is redundant.

I'm glad you brought that up! The syntax that allows omission of empty argument lists specifically for closures is to support elegant DSL-like representation at usage sites. This is a separate concern from how the function is declared.

The situation we have today can be summarized as follows:

Function declarations have parentheses around their argument lists, even when the list is empty. Function calls are the same, with the single exception that a function call that takes a single function type argument, called with a trailing closure literal, may omit the empty parentheses.

That second sentence is a single point of divergence for a specific use case that improves the elegance of the language; SwiftUI is a great example of that.

The recommendation in this thread is to diverge in two directions:

Function declarations have parentheses around their argument lists, except when the list is empty. Function calls are the same, but now with two exceptions: a function call that takes a single function type argument, called with a trailing closure literal, may omit the empty parentheses even though it must still be declared with the argument list inside them; and a function that takes no arguments may be declared without parentheses even though it may not be called without them.

Adding more exceptions to the syntax of the language makes it less consistent, and there should be good reasons for doing so. I simply don't think dropping two characters from a subset of function declarations is a big enough win.

7 Likes

Note that omitting return is to make the block statements consistent between func and closure. Closure, which prioritize brevity to certain extent, does have merit in doing that as well.

Omitting () has neither of those qualities, so omitting return wouldn’t be a good example.

Omitting () in function doesn’t make it consistent with compute property either. var doesn’t have () to begin with, so omitting it here would make it the sole exception.

Also for closure type declaration, we are forced to use () in the argument side (we even have explicit SE-0066 for that).

So there aren’t many places (if at all) that you can omit () on argument side

evolution/blob/master/proposals/0255-omit-return.md)

That was pretty much the entire reason for omiting returns

Doesn't this ignore a lot of details?

Current functions are spelled as:

  1. func
  2. Function name
  3. A list of a parameters
  4. An optional return Type -> Type
  5. Followed immediately by curly braces: { //do something }

This would simply modify rule 3 to: "An optional list of parameters in ()." This makes it more consistent with rule 4, and, again, does not change anything at the call site.

Anyway I'm done bike shedding

1 Like

Then isn't the argument against this a contradiction?

Which one?

Well, I could not disagree more. My first thought would be "w*f is that?! where does it come from?! at one point it was there and now it isn't!" and then I would frantically search google for explanations and waste several hours of my time... I remember doing so with ruby where you have the choice and I find that really confusing for new comers and/or people who don't just take things for granted and try to understand why something is the way it is.

5 Likes

Can we really take a decision based on a behavior that is self-described as "frantic"? ;-) It's funny and we're all nerdies at various degrees, but... Stating that the frictionless interpretation: "oh, I guess you don't have to make empty parameter lists anymore" can't be used right away by a rational being (or can't even be found - I'm not sure what you really mean) is far-reaching...

Edit: By "frictionless interpretation" I mean frictionless for the average reader of those forums. I certainly don't want to say it's frictionless for everybody, especially people who learn development. But those people wouldn't waste several hours of their time searching for an explanation of a fact they can't even describe.

4 Likes

SE-0066 is about type syntax when writing the type explicitly.

This thread aims to talk about making part of the func definition optional, they are not the same.

If we took SE-0066 and applied it to mean defining functions as well as function type syntax then omitting -> Void in func doSomething() { } would be a compile time error but clearly it is not.

Newcomers to the language would see this from the beginning, existing users of swift would be able to read the Change log.

It would be optional to omit them, so there would be no disruption at all to someone who was oblivious to this change happening.
If someone who didn't know about this change saw parameter-less functions in the wild IMO it would be obvious that the function takes no arguments and a quick search on the internet would lead them talk about the change.

I don't like the analogy to omitting the -> Void on a function return. Writing -> Void in a function definition implies to users, particularly those that are less experienced, that the function “returns something”. Since the function isn't marked @discardableResult, it's easy to misunderstand and think that the return is meaningful, or that you should write _ = f(
), etc. Omitting the return here makes the definition easier to understand, because you can also omit the code handling the return value at the call site.

The opposite is true of omitting the parentheses in function definitions, which could be misunderstood as meaning they can be omitted from call sites. This wouldn't be an unusual interpretation because it has precedent in other languages, where computed properties are just function calls where you can omit the parentheses at the call site. So I don't see how the very minor improvement in brevity is justified here.

4 Likes

Honestly, I don't like when there is 2 ways to write a very simple piece of code, especially for thing that don't bring a clear benefit, like omitting 2 parentheses in a declaration (declaration are far less used than call site).
This is an eternal source of question about what is the best way to write this, or what is the difference between this and that, and also will probably be a great source of flame wars about what is The Right Way to declare a function without argument.

I know Swift has already many places where this is the case (optional return statement, Optional field initialisation, 
), but it does not means adding a new one is a good thing.

11 Likes

My point is that two lines calling function f without arguments produces different values even if f as function is constant.

func f {return hiddenstate+=1}
f //gives two
f //gives three

Alright, I overlooked @allevato question to it and I'm likewise worried about the inconsistency between function definition without and function call with parentheses.

again...

Yes, but writing a function like:
func f {return something()}

reminds me rather to

let f:T=something()

than

let f:(Void)->T=()->something() //void closure

Sorry to be so blunt, but this thread is the definition of bikeshedding.
I find it a bit puzzling that such a minor issue has been the major topic of discussion on these forums for close to two weeks now.

7 Likes

There's your answer for why :smile:. It seems to be something small enough that it can become a whipping boy for places where people's preferences differ from the language. So people get pulled toward how they think/feel about those things, rather than addressing the actual proposal.

2 Likes

Swift is starting to look like a free for all. People wanted objects to have Function syntax.

class Foo {}
var f = Foo()
f(something: bar) <- Not an actual function but looks like one, not very clear and concise. But I understood why they wanted it.

They wanted classes to be callable as Functions.

class Foo{}
Foo(something: bar) <- Not an actual init function but looks like one. Definitely not “concise and clear”. It was out of not wanting to have to type a function name.

They wanted member lookup for members that don’t actually exist (dynamic members).

Now we are considering making it possible to just drop parentheses in the declaration because we don’t want to type them, knowing perfectly well that it makes it even less clear if we should be expecting it to act like a property or not.

I’m apologizing for how this might sound. Please understand I don’t mean to insult. But I really gotta say Swift is getting messy, unclear and full of syntactic sugar.

There is no reason to drop the parentheses. They help the reader know what is happening. It is about clarity. It’s already frustrating finding out that what looks like an initializer is not. And we can make things that aren’t declared act as if they are.

SwiftUI is turning into a mental mess. What looks like an if-statement is actually function underneath.

Getting off my tangent I don’t want to see parentheses dropped.

func myFunc() -> Bar
func myFunc -> Bar
var mf = myFunc //type is a reference to a func
var mf2 = myFunc() //type is Bar

func myFunc2 -> Bar
var mf = myFunc2 //type is a reference to a func
var mf2 = myFunc2() //type is Bar

when a new programmer sees (), they know that it needs to be called with the () as the func definition has (). It helps keep things recognizable and consistent. In the case of myFunc2 a new person reading it might not know how they have to have the parentheses. I know the original post sates that when called one had to have the parentheses.

But now some are almost suggesting we should drop it from the call all together and let it act like a property.

If you want it to act like a property, use a property. You won’t have to type the parentheses and save 0.03 seconds. At least people will know they are looking at an actual property and not just something that is acting like one.

11 Likes

Your critiques have already been made and counter-arguments given in this thread, so I'll just respond to this:

With what I just said.

I am only try to add a voice, because this is an issue I feel very passionately about. I want Swift to stay what I believe is orderly. Disregard if these points are not meaningful to you. I take no offense.

Removing () from function declarations does not provide new functionality and it deviates from what may be expected making it less clear -- in opposition one of the standing mandates of Swift. Clarity has often been described in terms of new users. New users expect that a declaration matches the call site pattern or have some sort of hint if otherwise (such as subscript keyword). This was the case in swift 1 where array declaration went from var myNumbers[]: Int to var myNumbers: [Int]. There was a lot of concern made around making patterns match swift declarations. Omitting the parentheses does not make the pattern match. Omitting the () becomes a syntactical oddity. It's also two characters.

OPINION
NOW, with that said, I have noticed that swift is no longer about clarity as the examples I have given demonstrated how unclear things can now become. The mandates may just need to change. That could be the future of Swift.

FACT
When reading code one can no longer be secure that what they are reading is a function, an object/struct initializer, or even something that has an actual declaration. (At least with previous proposals, there was new functionality added). With this proposal, one might not initially be sure if they need the parentheses at all since the declaration would not include it. It adds no new functionality.

Carry on. My protest is over. I appreciate everyone's work and insight to make this language amazing.

1 Like

This is kind of unrelated to the topic but I find it interesting :smiling_face:

I do understand frustration with the amount of complexity that is being added to the language. My thought process on this is these are advanced features built to enable things like SwiftUI/DSLs which would be difficult/impossible to build cleanly with "basic" Swift. Just as is true in other languages, advanced features are for advanced users. How many people write Java Annotations?

Adding features and capabilities to the language necessarily introduces some amount of complexity and variation/flexibility. Ideally new features won't negatively affect "basic" Swift, and I think thus far that's been generally true.

As a language matures, design and code standards become more important. Choosing the right tool for the job in a language is a decision a programmer has to make daily. Over-engineering is and will always be a problem. That doesn't mean tools shouldn't be added int situations where the language is insufficient. It's required for a language to evolve, enable new use cases, and remain relevant.

To summarize: No you shouldn't use one of these bad boys to prune your hedges. That doesn't mean they shouldn't be built. In the same way, "Idiomatic Swift" should be what most users interact with. The average Swift programmer should only see the end products of those advanced features.