Syntax Design - Pourquoi utiliser des parenthèses quand aucun argument n'est passé?

65

Dans de nombreuses langues, la syntaxe function_name(arg1, arg2, ...)est utilisée pour appeler une fonction. Lorsque nous voulons appeler la fonction sans aucun argument, nous devons le faire function_name().

Je trouve étrange qu'un compilateur ou un interpréteur de script ait besoin ()de le détecter avec succès en tant qu'appel de fonction. Si une variable est connue pour être appelable, pourquoi ne serait-il pas function_name;suffisant?

Par contre, dans certaines langues, on peut faire: function_name 'test';ou même function_name 'first' 'second';appeler une fonction ou une commande.

Je pense que les parenthèses auraient été meilleures si elles n’avaient été nécessaires que pour déclarer l’ordre de priorité. À d’autres endroits, elles étaient facultatives. Par exemple, faire if expression == true function_name;devrait être aussi valide que if (expression == true) function_name();.

La chose la plus agaçante à mon avis est de le faire 'SOME_STRING'.toLowerCase()quand clairement, la fonction prototype ne nécessite aucun argument. Pourquoi les concepteurs ont-ils décidé de ne pas utiliser le plus simple 'SOME_STRING'.lower?

Disclaimer: Ne vous méprenez pas, j'aime la syntaxe C-like! ;) Je demande simplement si cela pourrait être mieux. Requérir ()présente des avantages en termes de performances, ou facilite-t-il la compréhension du code? Je suis vraiment curieux de savoir quelle est exactement la raison.

David Refoua
la source
103
Que feriez-vous si vous vouliez passer une fonction en tant qu'argument à une autre fonction?
Vincent Savard
30
Tant que le code doit être lu par les humains, la lisibilité est primordiale.
Tulains Córdova
75
Vous posez la question à propos de la lisibilité, ()mais ce qui ressort dans votre message est la if (expression == true)déclaration. Vous vous inquiétez des superflus (), mais vous utilisez un superflu == true:)
David Arno
15
Visual Basic a permis cela
edc65
14
C'était obligatoire en Pascal, vous utilisiez des parenthèses uniquement pour les fonctions et les procédures qui prenaient des arguments.
RemcoGerlich

Réponses:

250

Pour les langages qui utilisent des fonctions de première classe , il est assez courant que la syntaxe de référence à une fonction soit la suivante:

a = object.functionName

alors que l'acte d' appeler cette fonction est:

b = object.functionName()

adans l'exemple ci-dessus serait une référence à la fonction ci-dessus (et vous pourriez l'appeler en faisant a()), tout en bcontenant la valeur de retour de la fonction.

Bien que certaines langues puissent faire des appels de fonction sans parenthèses, il peut être difficile de dire si elles appellent la fonction ou font simplement référence à la fonction.

Nathan Merrill
la source
9
Vous voudrez peut-être mentionner que cette différence n'est intéressante qu'en présence d'effets secondaires - peu importe la fonction
Bergi
73
@ Bergi: C'est important pour les fonctions pures! Si une fonction make_listregroupe ses arguments dans une liste, il y a une grande différence entre le f(make_list)passage d'une fonction à fune liste vide.
user2357112
12
@ Bergi: Même dans ce cas, j'aimerais quand même pouvoir passer SATInstance.solveou une autre fonction pure incroyablement coûteuse à une autre fonction sans essayer immédiatement de l'exécuter. Cela rend également les choses vraiment gênantes si someFunctionfait quelque chose de différent selon que l’ someFunctionon déclare qu’il est pur. Par exemple, si j'ai (pseudocode) func f() {return g}, func g() {doSomethingImpure}et func apply(x) {x()}puis les apply(f)appels f. Si je déclare alors que fc'est pur, alors soudain apply(f)passe gà apply, applyappelle get fait quelque chose d'impur.
user2357112
6
@ user2357112 La stratégie d'évaluation est indépendante de la pureté. Utiliser la syntaxe des appels comme annotation pour évaluer ce qui est possible (mais probablement gênant), mais cela ne change ni le résultat ni son exactitude. En ce qui concerne votre exemple apply(f), si gest impur (et applyaussi?), Alors tout est impur et se décompose bien sûr.
Bergi
14
Exemples: Python et ECMAScript, qui n’ont pas vraiment de concept de "méthodes". Vous avez plutôt une propriété nommée qui renvoie un objet fonction de première classe anonyme, puis vous appelez cette fonction de première classe anonyme. Spécifiquement, à la fois dans Python et dans ECMAScript, dire foo.bar()n'est pas une opération "méthode d'appel bard'objet foo" mais plutôt deux opérations, "déréférencer le champ bard'objet foo et ensuite appeler l'objet renvoyé à partir de ce champ". Ainsi, l' opérateur. est le sélecteur de champ et ()l' opérateur d'appel et ils sont indépendants.
Jörg W Mittag
62

En effet, Scala le permet, bien qu’une convention soit suivie: si la méthode a des effets secondaires, les parenthèses doivent néanmoins être utilisées.

En tant qu’écrivain du compilateur, la présence garantie de parenthèses me conviendrait fort bien; Je saurais toujours que c'est un appel à une méthode et je n'aurais pas à construire une bifurcation pour les cas impairs.

En tant que programmeur et lecteur de code, la présence de parenthèses ne laisse aucun doute sur le fait qu'il s'agit d'un appel de méthode, même si aucun paramètre n'est transmis.

Le passage de paramètres n'est pas la seule caractéristique qui définit un appel de méthode. Pourquoi devrais-je traiter une méthode sans paramètre différente de la méthode qui a des paramètres?

Robert Harvey
la source
Merci, et vous avez donné des raisons valables pour expliquer pourquoi c'est important. Pouvez-vous également donner quelques exemples expliquant pourquoi les concepteurs de langage devraient utiliser des fonctions dans des prototypes, s'il vous plaît?
David Refoua
la présence de parenthèses ne laisse aucun doute sur le fait que c’est une méthode d’appel que j’accepte totalement. Je préfère mes langages de programmation plus explicites qu'implicites.
Corey Ogburn
20
Scala est en réalité un peu plus subtile que cela: alors que d'autres langues n'autorisent qu'une liste de paramètres avec zéro paramètre ou plus, Scala n'autorise aucune liste de paramètres avec zéro paramètre ou plus. Donc, def foo()est une méthode avec une liste de paramètres vide, et def fooest une méthode sans liste de paramètres et les deux sont des choses différentes ! En général, une méthode sans liste de paramètres doit être appelée sans liste d'arguments et une méthode avec une liste de paramètres vide doit être appelée avec une liste d'arguments vide. Appeler une méthode sans liste de paramètres avec un argument vide est en fait…
Jörg W Mittag
19
… Interprété comme appelant la applyméthode de l'objet renvoyé par l'appel à la méthode avec une liste d'arguments vide, alors que l'appel d'une méthode avec une liste de paramètres vide sans liste d'arguments peut être interprété différemment en fonction du contexte, comme l'appelant la méthode avec une liste d'arguments vide, η-expansion dans une méthode partiellement appliquée (c.-à-d. "méthode de référence"), ou il pourrait ne pas compiler du tout. De plus, Scala suit le principe d'accès uniforme, en ne distinguant pas (syntaxiquement) entre appeler une méthode sans liste d'arguments et référencer un champ.
Jörg W Mittag
3
Même si j'apprécie Ruby pour l'absence de "cérémonie" requise - parens, accolades, type explicite - je peux dire que la méthode-parenthèse se lit plus rapidement ; mais une fois idiomatique familier, Ruby est assez lisible et plus rapide à écrire.
radarbob
19

Il s’agit en fait d’un choix assez subtil de choix de syntaxe. Je parlerai de langages fonctionnels, basés sur le calcul lambda typé.

Dans ces langues, chaque fonction a exactement un argument . Ce que nous considérons souvent comme des "arguments multiples" est en réalité un paramètre unique du type de produit. Ainsi, par exemple, la fonction qui compare deux entiers:

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

prend une seule paire d'entiers. Les parenthèses ne désignent pas les paramètres de la fonction; ils sont utilisés pour faire correspondre l'argument. Pour vous convaincre qu'il ne s'agit vraiment que d'un argument, nous pouvons utiliser les fonctions projectives au lieu du filtrage par motif pour déconstruire la paire:

leq some_pair = some_pair.1 <= some_pair.2

Ainsi, les parenthèses sont vraiment une commodité qui nous permet de mettre en correspondance des motifs et d’économiser du texte. Ils ne sont pas nécessaires.

Qu'en est-il d'une fonction qui n'a apparemment aucun argument? Une telle fonction a effectivement du domaine Unit. Le membre unique de Unitest habituellement écrit ainsi (), c'est pourquoi les parenthèses apparaissent.

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

Pour appeler cette fonction, il faudrait l'appliquer à une valeur de type Unit, qui doit être (), pour finir par écrire say_hi ().

Donc, il n’existe vraiment pas de liste d’arguments!

tête de jardin
la source
3
Et c’est la raison pour laquelle les parenthèses vides ()ressemblent à des cuillères moins le manche.
Mindwin
3
Définir une leqfonction qui utilise <plutôt que <=est assez déroutant!
Kryan
5
Cette réponse pourrait être plus facile à comprendre si elle utilisait le mot "tuple" en plus de phrases comme "type de produit".
Kevin
La plupart des formalismes traiteront int f (int x, int y) comme une fonction de I ^ 2 à I. Le calcul lambda est différent, il n'utilise pas cette méthode pour correspondre à ce qui est dans les autres formalismes l'arité élevée. À la place, le lambda calcul utilise le currying. La comparaison serait x -> (y -> (x <= y)). (x -> (y -> (x <= y)) 2 serait évalué à (y-> 2 <= y) et (x -> (y -> (x <= y)) 2 3 aurait été évalué à ( y-> 2 <= y) 3, qui est évalué à 2 <= 3.
Taemyr
1
@PeriataBreatta Je ne connais pas bien l'utilisation de cette image ()pour la représenter. Je ne serais pas surpris si au moins une partie de la raison était de ressembler à une "fonction sans paramètre". Cependant, il existe une autre explication possible pour la syntaxe. Dans l'algèbre des types, Unitc'est en fait l'identité des types de produits. Un produit binaire est écrit en tant que (a,b), nous pourrions écrire un produit unaire (a), une extension logique serait donc d'écrire le produit nullary simplement ().
Gardenhead
14

En Javascript par exemple, utiliser un nom de méthode sans () renvoie la fonction elle-même sans l'exécuter. De cette façon, vous pouvez par exemple transmettre la fonction en tant qu'argument à une autre méthode.

En Java, le choix a été fait qu'un identifiant suivi de () ou (...) désigne un appel de méthode, tandis qu'un identifiant without () se réfère à une variable membre. Cela pourrait améliorer la lisibilité, car vous ne doutez pas qu'il s'agisse d'une méthode ou d'une variable membre. En fait, le même nom peut être utilisé à la fois pour une méthode et pour une variable membre, les deux seront accessibles avec leur syntaxe respective.

Florian F
la source
4
+1 Tant que le code doit être lu par les humains, la lisibilité est primordiale.
Tulains Córdova
1
@ TulainsCórdova Je ne suis pas sûr que Perl soit d'accord avec vous.
Racheet
@Racheet: Perl ne peut pas mais le Perl Best Practicesfait
slebetman
1
@Racheet Perl est très lisible s'il est écrit pour être lisible. C'est la même chose pour à peu près toutes les langues non ésotériques. Bref, concis, Perl est très lisible par les programmeurs Perl, tout comme le code Scala ou Haskell est lisible par les personnes expérimentées dans ces langues. Je peux aussi vous parler allemand de manière très compliquée, grammaticalement correcte, mais vous ne comprendrez toujours pas un mot, même si vous maîtrisez l’allemand. Ce n'est pas la faute de l'allemand non plus. ;)
simbabque
en java, cela ne "identifie" pas une méthode. Ça l'appelle. Object::toStringidentifie une méthode.
njzk2
10

La syntaxe suit la sémantique, alors commençons par la sémantique:

Quelles sont les manières d'utiliser une fonction ou une méthode?

En réalité, il y a plusieurs façons:

  • une fonction peut être appelée, avec ou sans arguments
  • une fonction peut être traitée comme une valeur
  • une fonction peut être appliquée partiellement (créer une fermeture en prenant au moins un argument de moins et en fermant les arguments passés)

Avoir une syntaxe similaire pour des utilisations différentes est le meilleur moyen de créer un langage ambigu ou à tout le moins déroutant (et nous en avons assez).

En langage C et C-like:

  • l'appel d'une fonction se fait en utilisant des parenthèses pour entourer les arguments (il peut n'y en avoir aucun) comme dans func()
  • traiter une fonction comme une valeur peut être fait simplement en utilisant son nom comme dans &func(C créer un pointeur de fonction)
  • dans certaines langues, vous avez une syntaxe abrégée pour appliquer partiellement; Java permet someVariable::someMethodpar exemple (limité au récepteur de la méthode, mais toujours utile)

Notez que chaque utilisation utilise une syntaxe différente, ce qui vous permet de les distinguer facilement.

Matthieu M.
la source
2
Eh bien, "facilement" est l'une de ces situations "ce n'est clair que si c'est déjà compris". Un débutant aurait tout à fait raison de noter qu’il n’est nullement évident d’expliquer sans explication pourquoi une ponctuation a le sens qu’elle a.
Eric Lippert
2
@ EricLippert: En effet, facilement ne signifie pas nécessairement intuitif. Bien que dans ce cas, je voudrais souligner que les parenthèses sont également utilisées pour la fonction "invocation" en mathématiques. Cela étant dit, j'ai trouvé des débutants dans de nombreuses langues aux prises avec des concepts / sémantiques plus difficiles que la syntaxe en général.
Matthieu M.
Cette réponse confond la distinction entre "currying" et "application partielle". L' ::opérateur Java est un opérateur d'application partielle et non un opérateur de curry. L'application partielle est une opération qui prend une fonction et une ou plusieurs valeurs et renvoie une fonction acceptant moins de valeurs. Le currying ne fonctionne que sur les fonctions, pas sur les valeurs.
Periata Breatta
@PeriataBreatta: Bon point, corrigé.
Matthieu M.
8

Aucune des autres réponses n'a tenté de répondre à la question suivante: quelle redondance devrait-il y avoir dans la conception d'une langue? Parce que même si vous pouvez concevoir un langage de telle sorte que x = sqrt yx soit défini à la racine carrée de y, cela ne signifie pas nécessairement que vous devriez le faire.

Dans une langue sans redondance, chaque séquence de caractères signifie quelque chose, ce qui signifie que si vous faites une seule erreur, vous n'obtiendrez pas de message d'erreur, votre programme agira de manière erronée, ce qui peut être très différent de ce que vous destiné et très difficile à déboguer. (Comme le savent tous ceux qui ont travaillé avec des expressions rationnelles.) Un peu de redondance est une bonne chose, car elle permet de détecter bon nombre de vos erreurs. Plus il y a de redondance, plus les diagnostics seront précis. Maintenant, bien sûr, vous pouvez aller trop loin (aucun de nous ne veut écrire en COBOL ces jours-ci), mais il existe un juste équilibre.

La redondance facilite également la lisibilité, car il y a plus d'indices. Il serait très difficile de lire un anglais sans redondance, de même que les langages de programmation.

Michael Kay
la source
En un sens, je suis d’accord: les langages de programmation devraient avoir un «filet de sécurité». Mais je suis en désaccord que « un peu de redondance » dans la syntaxe est une bonne façon de s'y prendre - c'est passe- partout , et ce qu'il fait la plupart du temps est de générer du bruit qui fait des erreurs plus difficiles à repérer, et ouvre des possibilités pour les erreurs de syntaxe qui distraient de la vrais bugs. Le type de verbosité qui facilite la lisibilité a peu à voir avec la syntaxe, mais plutôt avec les conventions de dénomination . Et un filet de sécurité est de loin le plus efficacement mis en œuvre en tant que système de type fort , dans lequel la plupart des modifications aléatoires rendront le programme mal typé.
gauche vers
@leftaroundabout: J'aime réfléchir aux décisions de conception linguistique en termes de distance de Hamming. Si deux constructions ont des significations différentes, il est souvent utile de les différencier d'au moins deux manières et d'avoir une forme qui ne diffère que d'une manière qui génère un squawk du compilateur. Le concepteur de langage ne peut généralement pas être tenu responsable de la distinction des identifiants, mais si, par exemple, une assignation nécessite une :=comparaison ==, =son utilisation n'étant utilisable que pour l'initialisation à la compilation, la probabilité que des erreurs de frappe se transforment en assignations serait grandement réduite.
Supercat
@leftaroundabout: De même, si je concevais un langage, je devrais probablement avoir besoin ()de l'invocation d'une fonction à argument zéro, mais aussi d'un jeton pour "prendre l'adresse de" une fonction. La première action est beaucoup plus courante, il est donc inutile de sauvegarder un jeton dans le cas le moins fréquent.
Supercat
@ supercat: eh bien, encore une fois, je pense que cela résout le problème au mauvais niveau. L'attribution et la comparaison sont des opérations conceptuellement différentes, de même que l'évaluation de la fonction et la prise de l'adresse de la fonction, respectivement. Par conséquent, ils doivent avoir des types clairement distincts. Le fait que les opérateurs aient une distance de Hamming faible importe peu, car toute faute de frappe sera une erreur de type compilation.
gauche du
@leftaroundabout: si une affectation est en cours sur une variable de type booléen ou si le type de retour d'une fonction est lui-même un pointeur sur une fonction (ou - dans certaines langues - une fonction réelle), remplace la comparaison par une affectation, ou un appel de fonction avec une référence de fonction, peut générer un programme syntaxiquement valide mais dont la signification est totalement différente de celle de la version d'origine. La vérification de type trouvera de telles erreurs en général, mais rendre la syntaxe distincte semble être une meilleure approche.
Supercat
7

Dans une langue avec des effets secondaires, l’OMI est vraiment utile pour différencier la lecture d’une variable (en théorie, sans effets secondaires)

variable

et en appelant une fonction pouvant entraîner des effets secondaires

launch_nukes()

OTOH, s'il n'y a pas d'effets secondaires (autres que ceux encodés dans le système de types), il est alors inutile de faire la différence entre lire une variable et appeler une fonction sans argument.

Daniel Jour
la source
1
Notez que l'OP a présenté le cas où un objet est le seul argument et est présenté avant le nom de la fonction (qui fournit également une portée pour le nom de la fonction). noun.verbla syntaxe pourrait être aussi claire de sens noun.verb()et légèrement moins encombrée. De même, une fonction member_renvoyant une référence de gauche peut offrir une syntaxe conviviale par rapport à une fonction de définition habituelle: obj.member_ = new_valuevs obj.set_member(new_value)(le _suffixe est un indice qui dit "expose", rappelant les _ préfixes des fonctions "cachées").
Paul A. Clayton
1
@ PaulA.Clayton Je ne vois pas en quoi cela ne serait pas couvert par ma réponse: si l' noun.verbappel de fonction signifie, comment le différenciez-vous d'un simple accès membre?
Daniel Jour
1
En ce qui concerne la lecture de variables étant théoriquement exempt d’effets secondaires, je voudrais simplement dire ceci: méfiez-vous toujours des faux amis .
Justin Time 2 Réintégrer Monica
5

Dans la plupart des cas, il s’agit de choix syntaxiques de la grammaire du langage. Il est utile que la grammaire de chaque construction individuelle soit (relativement) non ambiguë quand elle est prise dans son ensemble. (S'il y a des ambiguïtés, comme dans certaines déclarations C ++, il doit y avoir des règles spécifiques pour la résolution.) Le compilateur n'a pas la latitude de deviner; il est nécessaire de suivre la spécification de langue.


Visual Basic, sous différentes formes, différencie les procédures qui ne renvoient pas de valeur et les fonctions.

Les procédures doivent être appelées en tant qu'énoncé et ne nécessitent pas de parenthèse, mais uniquement des arguments séparés par des virgules, le cas échéant. Les fonctions doivent être appelées dans le cadre d'une expression et requièrent les parenthèses.

C'est une distinction relativement inutile qui rend la refactorisation manuelle entre les deux formes plus douloureuse que nécessaire.

(D'autre part Visual Basic utilise les mêmes parens ()pour les références de tableau comme pour les appels de fonction, de sorte que les références de tableau ressemble à des appels de fonction. Et cela facilite refactoring manuelle d'un tableau dans un appel de fonction! Alors, nous pourrions réfléchir à d' autres langues utilisent []l » pour les références de tableau, mais je m'éloigne du sujet ...)


Dans les langages C et C ++, le contenu des variables est automatiquement accessible en utilisant leur nom. Si vous souhaitez faire référence à la variable elle-même au lieu de son contenu, vous appliquez l' &opérateur unaire .

Ce type de mécanisme pourrait également être appliqué aux noms de fonction. Le nom de la fonction brute pourrait impliquer un appel de fonction, alors qu'un &opérateur unaire serait utilisé pour désigner la fonction (elle-même) en tant que donnée. Personnellement, j'aime bien l'idée d'accéder à des fonctions sans argument sans effets secondaires sans la même syntaxe que les variables.

Ceci est parfaitement plausible (comme le sont d’autres choix syntaxiques pour cela).

Erik Eidt
la source
2
Bien sûr, alors que le manque de supports de base visuel sur les appels de procédure est une distinction inutile par rapport à ses appels de fonction, en ajoutant entre parenthèses nécessaires à leur serait une distinction inutile entre les déclarations , dont beaucoup dans un langage BASIC dérivés ont des effets qui sont très rappelle des procédures. Cela fait aussi longtemps que je n'ai pas travaillé dans une version MS BASIC, mais cela n’autorise-t-il pas encore la syntaxe call <procedure name> (<arguments>)? Je suis sûr que c’était une façon valable d’utiliser les procédures à l’époque où j’ai programmé QuickBASIC dans une autre vie ...
Periata Breatta
1
@PeriataBreatta: VB autorise toujours la syntaxe "call", et l'exige parfois dans les cas où l'appel est appelé (par exemple, on peut dire "Call If (someCondition, Delegate1, Delegate2) (Arguments)", mais directement l'invocation du résultat d'un opérateur "Si" ne serait pas valide en tant qu'instruction autonome].
Supercat
@PeriataBreatta J'ai aimé les appels de procédure sans crochet de VB6, car ils donnaient à un code de type DSL une belle apparence.
Mark Hurd
@supercat Dans LinqPad, il est étonnant de constater à quelle fréquence je dois utiliser Callune méthode où je n'ai presque jamais besoin de code de production "normal".
Mark Hurd
5

Cohérence et lisibilité.

Si j'apprends que vous appelez la fonction Xcomme ceci: X(arg1, arg2, ...), alors je pense que cela fonctionne de la même façon pour aucun argument: X().

Maintenant, en même temps, j'apprends que vous pouvez définir la variable comme un symbole et l'utiliser comme ceci:

a = 5
b = a
...

Maintenant, qu'est-ce que je penserais quand je trouverais ça?

c = X

Votre proposition est aussi bonne que la mienne. Dans des circonstances normales, le Xest une variable. Mais si nous prenions votre chemin, cela pourrait aussi être une fonction! Dois-je me rappeler si quel symbole correspond à quel groupe (variables / fonctions)?

Nous pourrions imposer des contraintes artificielles, par exemple "Les fonctions commencent par une lettre majuscule. Les variables commencent par une lettre minuscule", mais cela n'est pas nécessaire et complique les choses, même si cela peut parfois aider certains des objectifs de conception.

Note 1: Les autres réponses ignorent complètement une dernière chose: la langue peut vous permettre d'utiliser le même nom pour la fonction et la variable et de faire la distinction par contexte. Voir à Common Lisptitre d'exemple. Fonction xet variable xcoexistent parfaitement bien.

Side note 2: La réponse acceptée nous montre la syntaxe: object.functionname. Premièrement, les langages dotés de fonctions de premier ordre ne sont pas universels. Deuxièmement, en tant que programmeur, je considérerais cela comme une information supplémentaire: functionnameappartient à un object. Que ce objectsoit un objet, une classe ou un espace de noms importe peu, mais cela me dit qu'il appartient quelque part . Cela signifie que vous devez soit ajouter une syntaxe artificielle object.pour chaque fonction globale, soit en créer une objectpour contenir toutes les fonctions globales.

Et de toute façon, vous perdez la possibilité d'avoir des espaces de noms séparés pour les fonctions et les variables.

MatthewRock
la source
Ouaip. La cohérence est une raison suffisante, un point final (non que vos autres points ne soient pas valables - ils le sont).
Matthew lu
4

Pour ajouter aux autres réponses, prenons cet exemple C:

void *func_factory(void)
{
        return 0;
}

void *(*ff)(void);

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

Si l'opérateur d'invocation était facultatif, il n'y aurait aucun moyen de faire la distinction entre l'attribution de fonction et l'appel de fonction.

Ceci est encore plus problématique dans les langues dépourvues de système de type, par exemple JavaScript, où l'inférence de type ne peut pas être utilisée pour déterminer ce qui est ou non une fonction.

Mark K Cowan
la source
3
JavaScript ne manque pas de système de type. Il manque des déclarations de type . Mais il est tout à fait conscient de la différence entre les différents types de valeur.
Periata Breatta
1
Periata Breatta: le fait est que les variables et propriétés Java peuvent contenir des types de valeurs quelconques. Vous ne pouvez donc pas toujours savoir s'il s'agit d'une fonction au moment de la lecture du code.
Reinierpost
Par exemple, que fait cette fonction? function cross(a, b) { return a + b; }
Mark K Cowan
@MarkKCowan Il suit ceci: ecma-international.org/ecma-262/6.0/…
curdannii