68

In many languages, the syntax function_name(arg1, arg2, ...) is used to call a function. When we want to call the function without any arguments, we must do function_name().

I find it strange that a compiler or interpreter would require () in order to actually detect it as a function call. If something is known to be callable, why wouldn't function_name; be enough?

On the other hand, in some languages we can do: function_name 'test'; or even function_name 'first' 'second'; to call a function or a command.

I think parentheses would have been better if they were only needed to declare the order of priority, and in other places were optional. For example, doing if expression == true function_name; should be as valid as if (expression == true) function_name();.

An especially interesting case is writing 'SOME_STRING'.toLowerCase() when clearly no arguments are needed by the prototype function. Why did the designers decide against the simpler 'SOME_STRING'.lower design?

Disclaimer: Don't get me wrong, I quite love the C-like syntaxes! I'm just asking for the reasoning behind it. Does requiring () have any actual advantages, or does it simply make the code more human readable?

David Refoua
  • 1,111
  • 108
    What would you do if you wanted to pass a function as an argument to another function? – Vincent Savard Oct 05 '16 at 13:50
  • 4
    Indeed, or what if you wanted to assign a variable to a delegate? Eg. var myFunc = function; – Matthew Oct 05 '16 at 13:53
  • 2
    I'm fine with fun1( func2, 'test' ), but if we were to remove parentheses at all, I think func1 func2 'test' would be confusing as to which priority order should have been used. – David Refoua Oct 05 '16 at 13:54
  • 1
    Please also read the update in my question. – David Refoua Oct 05 '16 at 13:57
  • 1
    I read your last example (without parentheses) as "If expression is true, return this specific method delegate." It means something different than a method call, in other words. – Robert Harvey Oct 05 '16 at 14:00
  • 34
    As long as code is needed to be read by humans, readability is king. – Tulains Córdova Oct 05 '16 at 14:22
  • 81
    That's the thing with readability, you ask about (), yet the thing that stands out in your post is the if (expression == true) statement. You worry about superfluous ()'s, yet then use a superfluous == true :) – David Arno Oct 05 '16 at 14:41
  • 17
    Visual Basic allowed this – edc65 Oct 05 '16 at 14:44
  • 15
    It was mandatory in Pascal, you only used parens for functions and procedures that took arguments. – RemcoGerlich Oct 05 '16 at 14:54
  • 2
    And Perl mostly allows it, IIRC. – RemcoGerlich Oct 05 '16 at 14:57
  • 2
    @DRSDavidSoft Haskell has precisely that syntax and the rule that "function application binds more tightly then everything else", so f a b is the equivalent of f(a)(b) and not of f (a(b)). In that case this syntax is very useful because it can be used for partial application: plus x y = x+y; add_three = plus 3; add_three 5 -> 8. – Bakuriu Oct 05 '16 at 15:03
  • 4
    @edc65 that doesn't make it a good idea. In fact, any time a language feature is something that VB supported, I think that's an automatic entry in the "against" column – User1000547 Oct 05 '16 at 16:06
  • @mikeTheLiar it was partly borrowed from Pascal (see above). In fact, when the language only allows to call functions (not take a reference, a delegate or whatever else), having to add a couple of brackets containing nothing seems unreasonable – edc65 Oct 05 '16 at 16:10
  • 2
    @mikeTheLiar TIL that functions, namespaces, and error messages are among the many things with entries in the "against" column. – 8bittree Oct 05 '16 at 16:47
  • @8bittree I didn't say it was an automatic disqualification :) – User1000547 Oct 05 '16 at 16:47
  • @DavidArno dude, that expression is just an example! Would changing it to if (expr.something().length > 127) make you happy? :D jk man – David Refoua Oct 05 '16 at 16:50
  • @8bittree The sarcasm! – David Refoua Oct 05 '16 at 18:49
  • 1
    @VincentSavard "What would you do if you wanted to pass a function as an argument to another function" - Fortran solved that (non)-problem without using parentheses 50 years ago, and the basic idea of the solution still applies in the latest standard, Fortran 2008 (and most likely also in the Fortran 2015 standard when that is finally released). – alephzero Oct 05 '16 at 19:14
  • 2
    There are many non "curly-brace" syntaxes that this doesn't apply to. Concatenative languages (Factor, Forth, Joy, Postscript), or lispy languages (Scheme, Clojure, CL), or message languages (Smalltalk, Self). And then there's the chance your language has separate namespaces for values and functions (CL), or different contexts (Perl), for extra fun :P – fede s. Oct 05 '16 at 22:24
  • 5
    @alephzero Replacing () with CALL doesn't strike me as a particularly efficient replacement. In the end having functions as first class objects is incredibly useful and if you do that you need some way to distinguish between calling and referencing a function (you might just make it depend on the context of the expression - which can get confusing quickly -, but still there is something). There are different ways to do that, but there's little reason to introduce a weird distinction between function calls with parameters and without, particularly since you'll need something else instead. – Voo Oct 05 '16 at 23:06
  • Passing zero arguments is a different thing from not passing arguments! –  Oct 06 '16 at 01:53
  • 4
    @Hurkyl What? How? – Weaver Oct 06 '16 at 12:47
  • @StarWeaver You don't necessarily have to pass arguments to a function that takes arguments. This is used for currying or passing functions as arguments, for example. That's why plenty of functional languages distinguish between f (a function with no arguments) and f() (a function that is passed the unit argument), despite using e.g. f a b to pass two arguments to a function. – Luaan Oct 06 '16 at 13:10
  • 1
    Regarding "there are other ways to do it": in Matlab you call a function with funcname, and use @funcname when you want to pass it as an argument. I'm not saying that it's the best design choice in the world, but it works. One could even argue that the most common use should be the one that requires characters to type. – Federico Poloni Oct 06 '16 at 13:52
  • 1
    Ruby does this. It definitely makes for some nice, clean syntax when calling methods, but it does have the disadvantage of requiring the use of a separate built-in method for obtaining an object representing a method. – Ajedi32 Oct 06 '16 at 16:02
  • @Bakuriu Haskell also makes no distinction between a zero-argument function and its result – user253751 Oct 07 '16 at 00:44
  • @FedericoPoloni Sort of, but not always. In Matlab if you store an anonymous function in a variable you can call it just like a normal function or pass it without an @ as an argument. – Matt Oct 07 '16 at 01:13
  • @immibis There is, by definition, no such thing as a zero-argument function. – gardenhead Oct 07 '16 at 02:34
  • 1
    @gardenhead There is in many programming languages, but not in Haskell. – user253751 Oct 07 '16 at 02:41
  • @immibis Well, one could argue that a value of type IO something is a zero argument function in some way, but then passing it around doesn't call it, you have to extract the value inside the IO monad... in the end in imperative languages all the results are of that kind. – Bakuriu Oct 07 '16 at 06:39
  • 1
    @Bakuriu Other languages don't have "values of type IO something". You should stop thinking of every language in Haskell terms - saying that a zero-argument void-returning function in Java is a "value of type IO ()" is as silly as saying a named value in Haskell is a "memoized function call". – user253751 Oct 07 '16 at 09:02
  • 1
    @Bakuriu Java has these things that can have zero arguments, and they're called functions. Therefore, Java has zero-argument functions. – user253751 Oct 07 '16 at 09:03
  • (Actually I lied; Java calls them methods. s/Java/C/g) – user253751 Oct 07 '16 at 09:11
  • yeah... because if you don't, this happens : http://stackoverflow.com/q/26144675/327083 – J... Oct 07 '16 at 12:40
  • @VincentSavard I'd pass a function reference, of course. func1 \&func2 in Perl. Are you one of those silly Python users? – oals Oct 07 '16 at 13:22
  • So much talk and so many answers, yet no mention of Context Free Grammars. – RubberDuck Oct 08 '16 at 00:37
  • Because () is the execution of the closure – Jaquarh Oct 10 '16 at 18:20

10 Answers10

254

For languages that use first-class functions, its quite common that the syntax of referring to a function is:

a = object.functionName

while the act of calling that function is:

b = object.functionName()

a in the above example would be reference to the above function (and you could call it by doing a()), while b would contain the return value of the function.

While some languages can do function calls without parenthesis, it can get confusing whether they are calling the function, or simply referring to the function.

  • 9
    You might want to mention that this difference is only interesting in the presence of side effects - for a pure function it doesn't matter – Bergi Oct 05 '16 at 15:54
  • 76
    @Bergi: It matters a lot for pure functions! If I have a function make_list that packs its arguments into a list, there's a big difference between f(make_list) passing a function to f or passing an empty list. – user2357112 Oct 05 '16 at 16:03
  • 4
    @user2357112 In f(make_list) there are no arguments passed to make_list, so it would refer to the function. Of course make_list can't be variadic for that, or it would need to be inferable or explicitly specified how many parameters it is supposed to take. If it takes 0 parameters, there would indeed be no difference between the 0-ary function and the list. – Bergi Oct 05 '16 at 16:10
  • 12
    @Bergi: Even then, I'd still want to be able to pass SATInstance.solve or some other incredibly expensive pure function to another function without immediately attempting to run it. It also makes things really awkward if someFunction does something different depending on whether someFunction is declared to be pure. For example, if I have (pseudocode) func f() {return g}, func g() {doSomethingImpure}, and func apply(x) {x()}, then apply(f) calls f. If I then declare that f is pure, then suddenly apply(f) passes g to apply, and apply calls g and does something impure. – user2357112 Oct 05 '16 at 16:42
  • 6
    @user2357112 The evaluation strategy is independent from purity. Using the syntax of calls as an annotation when to evaluate what is possible (but probably awkward), however it doesn't change the result or its correctness. Regarding your example apply(f), if g is impure (and apply as well?) then the whole thing is impure and breaks down of course. – Bergi Oct 05 '16 at 16:52
  • 15
    Examples of this are Python and ECMAScript, both of which don't really have a core concept of "methods", instead you have a named property that returns an anonymous first-class function object and then you call that anonymous first-class function. Specifically, in both Python and ECMAScript, saying foo.bar() is not one operation "call method bar of object foo" but rather two operations, "dereference field bar of object foo and then call the object returned from that field". So, the . is the field selector operator and () is the call operator and they are independent. – Jörg W Mittag Oct 05 '16 at 18:52
  • 1
    As a real life example, I wrote a scripting language from scratch where just referring to the function called it. I did so because that matched the behavior of the previous scripting language. It continuously got in my way, causing all sorts of unusual syntax issues because I had no way to refer to a function itself. – Cort Ammon Oct 05 '16 at 19:25
  • 1
    Everything you say has its reason not in the way functions are called, but in the simplification of the language that omitting the parentheses signals "reference the function", not "the return value". The former is the least commonly used but easiest to write which is kind of illogical... – tofro Oct 05 '16 at 21:44
  • 1
    @Bergi It's not enough for the function to be pure; the result must be immutable as well. Otherwise, there is a semantic difference between calling the function once and modifying the result twice, and calling the function twice and modifying each result once. – Sebastian Redl Oct 06 '16 at 09:52
  • 1
    @SebastianRedl I'd say that is implied by purity. A mutable object needs to be instantiated/allocated, which is a side effect, and if a function returns two non-identical mutable objects for the same inputs it's no longer pure. A pure function can return non-identical but value-equal immutable results, or the same mutable object every time. – Bergi Oct 06 '16 at 10:52
  • @Bergi why do we consider the allocation of a mutable object to be a side effect, but not the allocation of an immutable object? – Kos Oct 06 '16 at 12:55
  • @Kos It all comes down to our definition of equality. We compare immutable objects by their values, not by their memory location ("identity") so it doesn't matter; their creation is pure. It's the same reason we consider numeric addition to be pure even if it changes the state of the cpu. – Bergi Oct 06 '16 at 13:01
  • 5
    An alternate way to disambiguate referencing a function and calling a function is to add an operator for referencing the function (e.g. VB.net's AddressOf). – 3Doubloons Oct 06 '16 at 13:49
  • 3
    @JörgWMittag not quite. Try var a = (document.querySelector); a("div") and observe the resulting error. Javascript does care about how you call a function. – John Dvorak Oct 06 '16 at 19:11
  • @JörgWMittag - EcmaScript's method invocation is very much a single operation of syntax <expr>.<identifier>(<expr-list>), because it performs three individual actions, rather than just the two you get by separating the operations as you suggest: it retrieves the value of the function from the object returned by the expression on the left, binds the this argument of the function to the value of the expression on the left, then executes the function with the arguments. Separating out the two operations doesn't bind this. It's one of the most annoying quirks of ecmascript semantics, IMO. – Periata Breatta Oct 07 '16 at 08:21
  • 2
    "While some languages can do function calls without parenthesis, it can get confusing whether they are calling the function, or simply referring to the function." -- of course, note that in a language with lazy evaluation, the two are effectively the same thing in any case. Calling a function isn't really an actual thing in such a language -- there is just reducing values to normal form, which happens when they're used, rather than where they're defined. – Periata Breatta Oct 07 '16 at 08:27
  • @PeriataBreatta: Strictly speaking, although the property access and function call operations are very closely tied together, there is no fused <expr>.<identifier>(<expr-list>) syntax for method calls, and you can even do a few weird things like (a.b)(c) or a['b'](c) that break the usual pattern and still get a as this. – user2357112 Oct 07 '16 at 20:14
  • @Bergi How can the difference be meaningless when I can pass a arguments but I can't pass b any arguments? The difference is whether I have a function to work with or I just have some result of whatever was called before. Which might not be what I want to call with now. – candied_orange Oct 16 '16 at 07:08
  • @CandiedOrange A function that takes no parameters like b already is a result in a pure language (e.g. Haskell) as it evaluates to only one value. "Calling a function" is meaningless when it doesn't have side effects, so it makes no difference whether it was called before or is called now. – Bergi Oct 16 '16 at 10:29
  • @Bergi That's simply not true. If we have a pure function defined by (define f function (lambda () 2)), then f is a function, but (f) is 2. They're certainly not the same thing! In Haskell, there's no confusion because there's no such thing as a nullary function: all functions take a single argument. The reason why Haskell doesn't need nullary functions is that (a) is has no side-effects, and (b) it's non-strict, so you don't need thunks to delay evaluation. You seem to be overgeneralizing from Haskell. In a pure language with strict evaluation, things would be different. – pyrocrasty Jan 24 '17 at 07:22
65

Indeed, Scala allows this, though there is a convention that is followed: if the method has side-effects, parentheses should be used anyway.

As a compiler writer, I would find the guaranteed presence of parentheses quite convenient; I would always know that is a method call, and I wouldn't have to build in a bifurcation for the odd case.

As a programmer and code reader, the presence of parentheses leaves no doubt that it is a method call, even though no parameters are passed.

The passing of parameters is not the sole defining characteristic of a method call. Why would I treat a parameter-less method any different from a method that has parameters?

Robert Harvey
  • 199,517
  • Thanks, and you gave valid reasons as to why this is important. Can you also give some examples as to why language designers should use functions in prototypes, please? – David Refoua Oct 05 '16 at 14:07
  • the presence of parentheses leaves no doubt that it is a method call I agree completely. I prefer my programming languages to more explicit than implicit. – Corey Ogburn Oct 05 '16 at 15:02
  • 22
    Scala is actually a bit more subtle than that: while other languages only allow one parameter list with zero or more parameters, Scala allows zero or more parameter lists with zero or more parameters. So, def foo() is a method with one empty parameter list, and def foo is a method with no parameter list and the two are different things! In general, a method with no parameter list needs to be called with no argument list and a method with an empty parameter list needs to be called with an empty argument list. Calling a method with no parameter list with an empty argument is actually … – Jörg W Mittag Oct 05 '16 at 18:04
  • 21
    … interpreted as calling the apply method of the object returned by the method call with an empty argument list, whereas calling a method with an empty parameter list without an argument list may depending on context be variously interpreted as calling the method with an empty argument list, η-expansion into a partially applied method (i.e. "method reference"), or it might not compile at all. Also, Scala follows the Uniform Access Principle, by not (syntactically) distinguishing between calling a method without an argument list and referencing a field. – Jörg W Mittag Oct 05 '16 at 18:07
  • 3
    Even as I come to appreciate Ruby for the absence of required "ceremony" - parens, braces, explicit type - I can tell that method-parenthesis reads faster; but once familiar idiomatic Ruby is quite readable and definitely faster to write. – radarbob Oct 06 '16 at 13:21
  • As a compiler writer, which I actually am, I would only need the parentheses if there was another meaning for the expression when they were absent, such as C and C++'s function-address semantic. Otherwise I wouldn't need them at all. – user207421 Oct 07 '16 at 00:19
  • @radarbob - Right. Once you get used to any syntactic convention it can be easy to read -- witness the scores of programmers who insist that Lisp is a language that can be used to write readable programs...! That said, Ruby's syntax is extremely good for writing fluent code, and especially embedded domain specific languages. I haven't worked with Scala yet, but I imagine that the same is true there, too. – Periata Breatta Oct 07 '16 at 08:35
  • Re "the presence of parentheses leaves no doubt that it is a method call": Like in C++. But it also leads to subtle bugs. *Accidentally* leaving () out of mouseClickCommon(); (as mouseClickCommon;) will *not* cause the compiler to complain by default (and function mouseClickCommon will not be called). Using compiler option -Wall detects it: "warning: statement is a reference, not call, to function ‘mouseClickCommon’ [-Waddress] ... warning: statement has no effect [-Wunused-value]" – Peter Mortensen Nov 12 '21 at 18:13
22

This is actually a pretty subtle fluke of syntax choices. I'll speak to functional languages, which are based on the typed lambda calculus.

In said languages, every function has exactly one argument. What we often think of as "multiple arguments" is actually a single parameter of product type. So for example, the function that compares two integers:

leq : int * int -> bool
leq (a, b) = a <= b

takes a single pair of integers. The parentheses do not denote the function parameters; they are used to pattern-match the argument. To convince you that this is really one argument, we can instead use the projective functions instead of pattern matching to deconstruct the pair:

leq some_pair = some_pair.1 <= some_pair.2

Thus, the parentheses really are a convenience that allows us to pattern match and save some typing. They are not required.

What about a function that ostensibly has no arguments? Such a function actually has domain Unit. The single member of Unit is usually written as (), so that's why the parentheses appear.

say_hi : Unit -> string
say_hi a = "Hi buddy!"

To call this function, we would have to apply it to a value of type Unit, which must be (), so we end up writing say_hi ().

So, there is really no such thing as an arguments list!

gardenhead
  • 4,747
  • 3
    And that is why empty parentheses () resemble spoons minus the handle. – Mindwin Remember Monica Oct 05 '16 at 18:45
  • 3
    Defining a leq function that uses < rather than <= is quite confusing! – KRyan Oct 05 '16 at 19:57
  • 9
    This answer might be easier to understand if it used the word "tuple" in addition to phrases like "product type." – Kevin Oct 06 '16 at 01:43
  • Most formalisms will treat int f(int x,int y) as a function from I^2 to I. Lambda calculus is different, it does not use this method to match what in other formalisms is high arity. Instead lambda calculus uses currying. Comparison would be x->(y->(x<=y)). (x->(y->(x<=y)) 2 would evaluate to (y->2<=y) and (x->(y->(x<=y)) 2 3 would evaluate to (y->2<=y) 3 which in turn evaluates to 2<=3. – Taemyr Oct 06 '16 at 07:49
  • @Taemyr I am using a type system with product types. This is where the analogy with "multiple arguments" in imperative languages stems from. Currying isn't very relevant here – gardenhead Oct 06 '16 at 14:06
  • @gardenhead I think you misunderstood my objection. Treating functions of high arity as functions with arity one and a high dimension domain is something several formalism does, also it's something that lambda calulus explicitly avoids by making the issue moot. – Taemyr Oct 06 '16 at 14:12
  • @Taemyr I still don't understand your objection :) I am not using the simply-typed lambda calculus. I am using a high-level functional programming language (Haskell), which has its roots in the lambda calculus. – gardenhead Oct 06 '16 at 14:16
  • While the point is an interesting one, is it not true that the common symbol for unit has been chosen to resemble the appearance of a function call in algol-derived languages? AFAIUI, the symbol first appeared in ML where it was likely added explicitly to allow for creating what would otherwise be zero-argument functions (ML has strict evaluation and allows for side-effecting functions but uses the single-argument-curried-function formalism, therefore such a form is a definite necessity for producing effeccts). The resemblance to a function call in algol would have been considered useful. – Periata Breatta Oct 07 '16 at 08:44
  • 1
    @PeriataBreatta I am not familiar with the history of using () to represent the unit. I would not be surprised if at least part of the reason was to resemble a "parameterless function". However, there is another possible explanation for the syntax. In the algebra of types, Unit is in fact the identity for product types. A binary product is written as (a,b), we could write a unary product as (a), so a logical extension would be to write the nullary product as simply (). – gardenhead Oct 07 '16 at 14:13
14

In Javascript for instance using a method name without () returns the function itself without executing it. This way you can for instance pass the function as an argument to another method.

In Java the choice was made that an identifier followed by () or (...) means a method call while an identifier without () refers to a member variable. This might improve readability, as you have no doubt whether you are dealing with a method or a member variable. In fact, the same name can be used both for a method and a member variable, both will be accessible with their respective syntax.

Florian F
  • 1,127
  • 1
  • 6
  • 13
  • 4
    +1 As long as code is needed to be read by humans, readability is king. – Tulains Córdova Oct 05 '16 at 14:22
  • 1
    @TulainsCórdova I'm not sure perl agrees with you. – Racheet Oct 05 '16 at 16:36
  • @Racheet: Perl may not but Perl Best Practices does – slebetman Oct 06 '16 at 04:15
  • 1
    @Racheet Perl is very readable if it's written to be readable. That's the same for pretty much every non-esoteric language. Short, concise Perl is very readable to Perl programmers, just like Scala or Haskell code is readable to people experienced in those languages. I can also speak German to you in a very convoluted manner that is grammatically completely correct, but still you won't understand a word, even if you're proficient in German. That's not German's fault either. ;) – simbabque Oct 06 '16 at 10:39
  • in java it does not "identify" a method. It calls it. Object::toString identifies a method. – njzk2 Oct 06 '16 at 21:23
  • @njzk2 The answer specifically stated that in Java .a identifies a member variable and .a() identifies a function call. It does not explain how to identify a method pointer, but that's not necessary for its completeness. – Kroltan Oct 07 '16 at 02:03
  • @Kroltan in the context of the question, (i.e. not when declaring or defining members and methods), () identifies a method call, like you said. But this answer states that it identifies a method. There is a huge difference, which has been pointed out by other answers. – njzk2 Oct 07 '16 at 02:49
  • @njzk2 Right. I updated my answer. – Florian F Oct 07 '16 at 08:16
11

Syntax follows semantics, so let's start from semantics:

What are the ways of using a function or method?

There are, actually, multiple ways:

  • a function can be called, with or without arguments
  • a function can be treated as a value
  • a function can be partially applied (creating a closure taking at least one less argument and closing over the passed arguments)

Having a similar syntax for different uses is the best way to create either an ambiguous language or at the very least a confusing one (and we have enough of those).

In C, and C-like languages:

  • calling a function is done by using parentheses to enclose the arguments (there might be none) as in func()
  • treating a function as a value can be done by simply using its name as in &func (C creating a function pointer)
  • in some languages, you have short-hand syntaxes for partially applying; Java allows someVariable::someMethod for example (limited to the method receiver, but still useful)

Note how each usage features a different syntax, allowing you to tell them apart easily.

Matthieu M.
  • 14,896
  • 2
    Well, "easily" is one of those "it's clear only if its already understood" situations. A beginner would be entirely justified in noting that it is by no means obvious without explanation why some punctuation has the meaning it has. – Eric Lippert Oct 05 '16 at 17:08
  • 2
    @EricLippert: Indeed, easily does not necessarily mean intuitive. Though in this case I would point that parentheses are also used for function "invocation" in mathematics. That being said, I've found beginners in many languages struggling more with concepts/semantics than syntax in general. – Matthieu M. Oct 05 '16 at 17:20
  • This answer confuses the distinction between "currying" and "partial application". The :: operator in Java is a partial application operator, not a currying operator. Partial application is an operation that takes a function and one or more values and returns a function accepting fewer values. Currying operates only on functions, not values. – Periata Breatta Oct 07 '16 at 08:55
  • @PeriataBreatta: Good point, fixed. – Matthieu M. Oct 07 '16 at 09:32
10

None of the other answers have attempted to tackle the question: how much redundancy should there be in the design of a language? Because even if you can design a language so that x = sqrt y sets x to the square root of y, that doesn't mean you necessarily should.

In a language with no redundancy, every sequence of characters means something, which means that if you make a single mistake, you won't get an error message, your program will do the wrong thing, which might be something very different from what you intended and very difficult to debug. (As anyone who has worked with regular expressions will know.) A little redundancy is a good thing because it enables many of your errors to be detected, and the more redundancy there is, the more likely it is that the diagnostics will be accurate. Now of course, you can take that too far (none of us want to write in COBOL these days), but there is a balance that is right.

Redundancy also helps readability, because there are more clues. English without redundancy would be very hard to read, and the same is true of programming languages.

Michael Kay
  • 3,474
  • 1
    In a sense I agree: programming languages should have a “safety net” mechanism. But I disagree that “a little redundancy” in the syntax is a good way to go about it – that's boilerplate, and what it mostly does is introduce noise which makes errors harder to spot, and opens up possibilities for syntax errors which distract from the real bugs. The kind of verbosity that aids readability has little to do with syntax, more with naming conventions. And a safety net is by far most efficiently implemented as a strong type system, in which most random changes will make the program ill-typed. – leftaroundabout Oct 09 '16 at 22:55
  • @leftaroundabout: I like to think about language design decisions in terms of Hamming distance. If two constructs have different meanings, it's often helpful to have them differ in at least two ways, and have a form which differs in only one way generate a compiler squawk. The language designer generally can't be responsible for ensuring that identifiers are easily distinguishable, but if e.g. assignment requires := and comparison ==, with = only being usable for compile-time initialization, then the likelihood of typos turning comparisons into assignments would be greatly diminished. – supercat Oct 10 '16 at 15:50
  • @leftaroundabout: LIkewise, if I were designing a language, I would likely require () for invocation of a zero-argument function but also require a token to "take the address of" a function. The former action is much more common, so saving a token in the less common case isn't all that useful. – supercat Oct 10 '16 at 15:53
  • @supercat: well, again I think this is solving the problem on the wrong level. Assignment and comparison are conceptually different operations, so are function evaluation and function-adress taking, respectively. Hence they should have clearly distinct types, then it doesn't really matter anymore if the operators have a low Hamming distance, because any typo will be a compile-time type error. – leftaroundabout Oct 10 '16 at 16:03
  • @leftaroundabout: If an assignment is being made to a boolean-type variable, or if a function's return type is itself a pointer to a function (or--in some languages--an actual function), replacing a comparison with an assignment, or a function invocation with a function reference, may yield a program which is syntactically valid, but has totally different meaning from the original version. Type checking will find some such errors in general, but making the syntax distinct seems like a better approach. – supercat Oct 10 '16 at 16:25
  • An assignment to a boolean-type variable would not have boolean type. Its type would be a state monad, or rather a lens into the state object of such a monad. — The result of a function f can sure be a function again, but not a function with the same type as f itself (unless the language allows infinite types). – leftaroundabout Oct 10 '16 at 16:32
7

In a language with side effects it's IMO really helpful to differentiate between (in theory side effect free) reading of a variable

variable

and calling a function, which might incur side effects

launch_nukes()

OTOH, if there are no side effects (other than those encoded in the type system) then there's no point to even differentiate between reading a variable and calling a function with no arguments.

  • 1
    Note that the OP presented the case where an object is the only argument and is presented before the function name (which also provides a scope for the function name). noun.verb syntax could be as clear in meaning as noun.verb() and slightly less cluttered. Similarly, a function member_ returning a left reference might offer a friendly syntax than a typical setter function: obj.member_ = new_value vs. obj.set_member(new_value) (the _ postfix is a hint saying "exposed" reminiscent of _ prefixes for "hidden" functions). –  Oct 06 '16 at 01:02
  • 1
    @PaulA.Clayton I don't see how this isn't covered by my answer: If noun.verb means function invocation, how do you differentiate it from mere member access? – Daniel Jour Oct 06 '16 at 07:52
  • 1
    In regards to reading variables being theoretically free of side effects, I just want to say this: Always beware of false friends. – Justin Time - Reinstate Monica Oct 06 '16 at 23:16
5

In most cases, these are syntactic choices of the grammar of the language. It is useful for grammar for the various individual construct to be (relatively) unambiguous when taken all together. (If there are ambiguities, like in some C++ declarations, there have to be specific rules for resolution.) The compiler doesn't have the latitude for guessing; it is required to follow the language specification.


Visual Basic, in various forms, differentiates between procedures that don't return a value, and functions.

Procedures must be called as a statement, and don't require parens, just comma separated arguments, if any. Functions must be called as part of an expression, and require the parens.

It is a relatively unnecessary distinction that makes manual refactoring between the two forms more painful than it has to be.

(On the other hand Visual Basic uses the same parens () for array references as for function calls, so array references look like function calls. And this eases manual refactoring of an array into a function call! So, we could ponder other languages use []'s for array references, but I digress...)


In the C and C++ languages the contents of variables are automatically accessed by using their name, and if you want to refer to the variable itself instead of its contents, you apply the unary & operator.

This kind of mechanism could also be applied to function names. The raw function name could imply a function call, whereas a unary & operator would be used to refer to the function (itself) as data. Personally, I like the idea accessing side-effect-free no-argument functions with the same syntax as variables.

This is perfectly plausible (as are other syntactic choices for this).

Erik Eidt
  • 33,747
  • 2
    Of course, while visual basic's lack of brackets on procedure calls is an unnecessary distinction in comparison with its function calls, adding required brackets to them would be an unnecessary distinction between statements, many of which in a BASIC-derived language have effects that are very reminiscent of procedures. It's also been a while since I did any work in an MS BASIC version, but does it not still allow the syntax call <procedure name> (<arguments>)? Pretty sure that was a valid way of using procedures back when I did QuickBASIC programming in another lifetime... – Periata Breatta Oct 07 '16 at 09:01
  • 1
    @PeriataBreatta: VB still does allow the "call" syntax, and sometimes requires it in cases where the thing to be called is an expression [e.g. one can say "Call If(someCondition, Delegate1, Delegate2)(Arguments)", but direct invocation of the result of an "If" operator would not be valid as a standalone statement]. – supercat Oct 10 '16 at 16:28
  • @PeriataBreatta I liked VB6's no bracket procedure calls because it made DSL-like code look good. – Mark Hurd Oct 14 '16 at 07:19
  • @supercat In LinqPad, it is amazing how often I need to use Call for a method where I almost never need to in "normal" production code. – Mark Hurd Oct 14 '16 at 07:19
5

Consistency and readability.

If I learn that you call the function X like this: X(arg1, arg2, ...), then I expect it to work the same way for no arguments: X().

Now at the same time I learn that you can define the variable as some symbol, and use it like this:

a = 5
b = a
...

Now what would I think when I find this?

c = X

Your guess is as good as mine. In normal circumstances, the X is a variable. But if we were to take your path, it could also be a function! Am I to remember whether which symbol maps to which group(variables/functions)?

We could impose artificial constraints, e.g. "Functions start with capital letter. Variables start with lower-case letter", but that's unneccessary and makes things more complicated, although might sometimes help some of the design goals.

Side-note 1: Other answers completely ignore one more thing: language might allow you to use the same name for both function and variable, and distinguish by context. See Common Lisp as an example. Function x and variable x coexist perfectly fine.

Side-note 2: The accepted answer shows us the syntax: object.functionname. Firstly, it's not universal to languages with first-class functions. Secondly, as a programmer I would treat this as an additional information: functionname belongs to an object. Whether object is an object, a class or a namespace doesn't matter that much, but it tells me that it belongs somewhere. This means you have to either add artificial syntax object. for each global function or create some object to hold all global functions.

And either way, you lose the ability to have separate namespaces for functions and variables.

4

To add to the other answers, take this C example:

void *func_factory(void)
{
        return 0;
}

void *(*ff)(void);

void example()
{
        ff = func_factory;
        ff = func_factory();
}

If the invocation operator was optional, there would be no way to distinguish between the function assignment and the function call.

This is even more problematic in languages lacking a type system, e.g. JavaScript, where type inference cannot be used to figure out what is and isn't a function.

  • 3
    JavaScript does not lack a type system. It lacks type declarations. But it's entirely aware of the difference between different types of value. – Periata Breatta Oct 07 '16 at 09:03
  • 1
    Periata Breatta: the point is that Java variables and properties may contain types of any value, so you can't always know whether or not it is a function at code reading time. – reinierpost Oct 07 '16 at 10:17
  • For example, what does this function do? function cross(a, b) { return a + b; } – Mark K Cowan Oct 07 '16 at 10:24
  • @MarkKCowan It follows this: http://www.ecma-international.org/ecma-262/6.0/#sec-addition-operator-plus-runtime-semantics-evaluation – curiousdannii Oct 08 '16 at 13:30