Allow function definitions to omit parentheses if no parameters

The original post is asking for a feature where we can omit characters in direct opposition to the efforts that were put into Swift to keep patterns matched for both declarations and call sites. Suggesting that it affects clarity and security of what is being done in code is most definitely not off topic as it affects what was a major guiding principal of the language. The removal of the parentheses on function declarations does not add new functionality and does not increase clarity. The omission of () is therefor not even classifiable as a tool, but rather a decoration.

I provided examples of such concern to maintain a clear argument for the counter position and demonstrate case precedence.

The language is pretty mature, unless a feature increase functionality while maintaining clarity, I vote no. I rest my case for now. Thanks for all the input. It is greatly appreciated.

4 Likes

Again, there are already counterarguments to your critiques in this thread and I'm not going to re-iterate them.

I was responding to your general feelings about the direction of the language.

Swift hasn't even been ABI stable for a year, and is <5 years old. Given your (and others) apparent feelings about the changes going on in the language, seems like it's going through it's rebellious teenager phase :smile:

1 Like

Exactly.

The intention of number 4 is that you also do not have to set its return to a variable. Number 4 is consistent with an intended use of a return. If you were to make number 3 work like number 4, then number 3 would have to consistent with its use, namely removing () from the function call as well. As number 4 removes the requirement to set its return value to a variable.

There are a lot of really good points for and against this proposal on this thread. I really appreciate all of the feedback and it is really motivating to see so many people excited about talking about the direction of the language.

This is a very minor but often occurring pattern in question, in my day to day work as a developer I have to write functions and methods that take no arguments all the time, so even though some people are counting the value of the proposed change by the number of characters typed before and after I think we should think about the impact of this proposed change a little differently.

Take for example the unary increment and decrement operators ++ and -- these operators were removed from the language very early on because it was a special case for something that could easily be expressed as += 1 or -= 1 the argument for ++ and -- to exist was very week and was simply a throwback to C.

It made no sense to have a special case for the event of adding one with ++ when that could be expressed with an already existing shorthand operator += and the argument 1.

Semantically if syntax serves no purpose it should not be mandatory.
func is enough to symbolize a function () provides no value when defining a function when there are no parameters.

... Now speaking of edge cases some people are calling omitting () in

func foo { ... }

An edge case, but did the empty parameter list belong there in the first place?
What purpose do they serve?
func clearly is written declaring this to be a function and we can omit -> Void for convenience so why not do away with requiring explicit empty parameter lists at function definition?

Why not make it so that parameter lists are only required when there are actual parameters present?

Between:

 func foo () -> Void { ... }

and

 func bar ()  { ... }

The bar seems like a cleaner syntax.

Between:

 func baz () { ... }

and

 func qux { ... }

To me personally qux is easier on the eyes.

Once again you would call these all the same:

foo()
bar()
baz()
qux()

Nothing would change in the way you call functions because () is the operator that calls the function.
When calling the function () has meaning when defining the function it does not IMO.

Remember, Void is a type.

func foo() { ... }

is not the same as

func foo(_ v: Void) { ... }
3 Likes

Sorry for my loose language! Yes good point!
Let me edit what I wrote up top.

One more analogy: Single Value Tuples don't exist in Swift because they are pointless.

var foo = ("Blue")
var bar = ("Blue", "Red")
bar.0 // This is valid
foo.0 // This will crash

foo can be declared but when evaluated becomes a String since a tuple with length 1 does not serve any purpose tuples of size 1 are not permitted in the language but bar which has two is perfectly valid because it is packaged with something else.

I see a parallel between how pointless syntax like ("Blue") and func foo() { ... } are, single value tuples just add noise to the value they wrap, they provide not value and no meaning.
In a similar way I see func foo() { ... } as noise when it could be written as func foo { ... } with absolutely no loss of information.

And func could be written as fun. There's ample precedent in other languages. I agree that there's no loss of clarity if parentheses are omitted, but I also think that having two ways to write the same thing adds needless complexity. I think you have to show that empty parentheses are actively harmful if you want this pitch to gain traction.

12 Likes

It does serve a purpose and it does provide value - it tells you explicitly and unambiguously that the function takes no arguments.

This function...

func noArgs() { ... }

...explicitly has no parameters. Whereas this function...

func noArgs { ... }

...takes no arguments? Takes an undefined list of arguments? Is variadic? Is dynamic in some way? Is a placeholder or extension point that gets filled in elsewhere? Might be a syntax we want to reserve for some future feature of the language?

4 Likes

Normally,
both functions should be the same even if Void is a type, but I think swift has a special and wrong meaning of Void.
If Void is a empty tuple then it represents bottom, a type which cannot be instantiated by any type.

It doesn't represent a bottom type. Void is a bit special in Swift since it is actually just (), which is both the type of the 0-tuple, and how you write an instance of the 0-tuple in Swift, as this is valid code:

var a: () = ()

At the logical level, there's nothing stopping you from creating an instance of the 0-tuple, unlike an uninhabited enum. Now I have no idea what the language actually does with the 0-tuple at runtime.

2 Likes

I think you're confusing the representation of Void with the (single) value for the Void type. The representation is the same as for an empty tuple, but that doesn't mean that an empty tuple is a value of the Void type.

5 Likes

From my understanding, Void and the empty tuple are interchangeable.

func testing() -> () {
  return ()
}

func testing2() {
  return ()
}

func testing3() -> Void {
  return ()
}


print(type(of: testing) == type(of: testing2)) // true
print(type(of: testing) == type(of: testing3)) // true

And IIRC Void is actually just a typealis for the empty tuple. Edit: @xwu linked that this is true

This is the sole reason I’ve stopped using Void altogether and just write ()

Alright, () is context dependent.
Why this call won't work:

func testing() -> () {
  return ()
}

let a=testing(())

Clearly test(()) describes an application of test with (()) but it has to decay to () which is indeed the case by type checking ( type(of:)). However, passing the Void instance explicit seems not to be allowed.

() -> () and (()) -> () are distinct types in Swift, and have been since implementation of SE-0110.

6 Likes

If you think that func foo { ... } is in bad taste then you must also be against this func foo() { ... } and support this instead: func foo() -> Void { ... }

1 Like

I still strongly disagree with this line of reasoning and don't see the two as similar at all.

8 Likes