Pourquoi la plupart des langages de programmation ont-ils une syntaxe ou un mot-clé spécial pour la déclaration de fonctions? [fermé]


39

La plupart des langages de programmation (à la fois typés dynamiquement et statiquement) ont des mots-clés et / ou une syntaxe spéciaux qui ont une apparence très différente de la déclaration de variables pour la déclaration de fonctions. Je vois les fonctions comme déclarant simplement une autre entité nommée:

Par exemple en Python:

x = 2
y = addOne(x)
def addOne(number): 
  return number + 1

Pourquoi pas:

x = 2
y = addOne(x)
addOne = (number) => 
  return number + 1

De même, dans un langage comme Java:

int x = 2;
int y = addOne(x);

int addOne(int x) {
  return x + 1;
}

Pourquoi pas:

int x = 2;
int y = addOne(x);
(int => int) addOne = (x) => {
  return x + 1;
}

Cette syntaxe semble une manière plus naturelle de déclarer quelque chose (que ce soit une fonction ou une variable) et un mot-clé de moins comme defou functiondans certaines langues. Et, IMO, il est plus cohérent (je regarde au même endroit pour comprendre le type d’une variable ou d’une fonction) et rend probablement l’analyseur / la grammaire un peu plus simple à écrire.

Je sais que très peu de langages utilisent cette idée (CoffeeScript, Haskell), mais la plupart des langages ont une syntaxe spéciale pour les fonctions (Java, C ++, Python, JavaScript, C #, PHP, Ruby).

Même en Scala, qui prend en charge les deux méthodes (et comporte une inférence de type), il est plus courant d'écrire:

def addOne(x: Int) = x + 1

Plutôt que:

val addOne = (x: Int) => x + 1

OMI, au moins à Scala, c'est probablement la version la plus compréhensible, mais cet idiome est rarement suivi:

val x: Int = 1
val y: Int = addOne(x)
val addOne: (Int => Int) = x => x + 1

Je travaille sur mon propre langage des jouets et je me demande s’il existe des pièges si je conçois mon langage de cette manière et s’il existe des raisons historiques ou techniques pour lesquelles ce modèle n’est pas largement suivi?


9
La raison est probablement historique. Celui qui l'a fait le premier l'a fait ainsi, et tous les autres l'ont copié. Mais je doute que nous puissions être certains.

29
Je pense que c'est parce qu'une fonction ou une méthode n'est simplement pas une autre entité nommée . Dans les langages fonctionnels, ils sont (vous pouvez les faire circuler, etc.), mais dans d’autres langages (comme Java), une fonction ou une méthode est quelque chose de tout à fait différent d’une simple variable et ne peut pas être traité comme tel (j’admets que Java 8 l’affaiblit déclaration), il est donc logique de définir les fonctions / méthodes différemment, car elles se comportent différemment.
11684

15
Je ne suis pas d'accord pour dire que votre proposition est une manière plus naturelle de déclarer des fonctions. Je n'aime vraiment pas la syntaxe de Coffeescript, et une partie de cela est liée à sa syntaxe de déclaration de fonction. Le fait est que, si ce n’est pas cassé, ne le répare pas. Écrire «def» ou «function» n’est pas une mince affaire, il est bien plus évident qu’un simple coup d’œil par rapport à certains symboles fantaisistes de type Perl qui, avec des yeux irréfléchis ou fatigués, pourraient facilement être confondus avec autre chose. Sur une note tout à fait personnelle, je pense également que la syntaxe suggérée dans les exemples semble beaucoup plus laide que la syntaxe actuelle.
Roy

22
En quoi '=>' n'est-il pas un "mot clé ou une syntaxe spéciale pour déclarer une fonction"?
TML

11
Je ne sais pas pour vous, mais pour moi, (int => int) addOne = (x) => {c'est beaucoup plus "spécial" et "complexe" que int addOne(int) {...
Bogdan Alexandru

Réponses:


48

Je pense que la raison en est que la plupart des langues populaires proviennent ou ont été influencées par la famille de langues C, par opposition aux langues fonctionnelles et à leur racine, le lambda calcul.

Et dans ces langages, les fonctions ne sont pas simplement une autre valeur:

  • En C ++, C # et Java, vous pouvez surcharger des fonctions: vous pouvez avoir deux fonctions avec le même nom, mais une signature différente.
  • En C, C ++, C # et Java, vous pouvez avoir des valeurs qui représentent des fonctions, mais les pointeurs de fonction, les foncteurs, les délégués et les interfaces fonctionnelles sont tous distincts des fonctions elles-mêmes. Une partie de la raison est que la plupart de ces fonctions ne sont pas en réalité des fonctions, elles sont une fonction associée à un état (modifiable).
  • Les variables sont modifiables par défaut (vous devez utiliser const, readonlyoufinal pour interdisez mutation), mais les fonctions ne peuvent pas être réaffectés.
  • D'un point de vue plus technique, le code (composé de fonctions) et les données sont séparés. Elles occupent généralement différentes parties de la mémoire et on y accède différemment: le code est chargé une seule fois, puis exécuté (mais non lu ni écrit), alors que les données sont souvent allouées et désallouées en permanence et sont écrites et lues mais jamais exécutées.

    Et comme C était censé être "proche du métal", il est également logique de refléter cette distinction dans la syntaxe du langage.

  • L'approche "fonction n'est qu'une valeur" qui constitue la base de la programmation fonctionnelle n'a gagné du terrain que récemment dans les langages communs, comme en témoigne l'introduction tardive de lambdas en C ++, C # et Java (2011, 2007, 2014).


12
C # avait des fonctions anonymes - qui sont juste des lambdas sans inférence de type et une syntaxe maladroite - depuis 2005.
Eric Lippert le

2
"D'un point de vue plus technique, le code (composé de fonctions) et les données sont séparés" - certaines langues, le plus bien en connaissance de cause, les LISP ne font pas cette séparation et ne traitent pas vraiment le code et les données de manière très différente. (Les LISP sont l'exemple le plus connu, mais de nombreux autres langages comme REBOL le font)
Benjamin Gruenbaum Le

1
@BenjaminGruenbaum Je parlais du code compilé en mémoire, pas du niveau de langue.
svick

C a des pointeurs de fonction qui, au moins légèrement, peuvent être considérés comme une valeur supplémentaire. Une valeur dangereuse à déconner, mais des choses étranges se sont produites.
Patrick Hughes

@ PatrickHughes Oui, je les mentionne dans mon deuxième point. Mais les pointeurs de fonction sont assez différents des fonctions.
svick

59

C'est parce qu'il est important que les humains reconnaissent que les fonctions ne sont pas simplement "une autre entité nommée". Parfois, il est logique de les manipuler en tant que tels, mais ils peuvent toujours être reconnus en un coup d'œil.

Peu importe ce que l'ordinateur pense de la syntaxe, car un blob incompréhensible est très bien pour une machine à interpréter, mais ce serait presque impossible à comprendre et à entretenir pour les humains.

C'est vraiment la même raison que celle pour laquelle nous avons des boucles while et for, switch et if else, etc., même si toutes finissent par se résumer à une instruction de comparaison et de saut. La raison en est parce que c'est là pour le bénéfice des humains qui maintiennent et comprennent le code.

Avoir vos fonctions en tant qu '"une autre entité nommée" comme vous le proposez rendra votre code plus difficile à visualiser, et donc à comprendre.


12
Je suis en désaccord que le traitement fonctionne comme des entités nommées nécessairement rend le code plus difficile à comprendre. Cela est presque certainement vrai pour tout code qui suit un paradigme procédural, mais cela peut ne pas être vrai pour un code qui suit un paradigme fonctionnel.
Kyle Strand le

29
"C’est vraiment la même raison que celle pour laquelle nous avons des boucles while et for, switch et if else, etc, même si toutes se résument finalement à une instruction de comparaison et de saut. " +1 à cela. Si tous les programmeurs étaient à l'aise avec le langage machine, nous n'aurions pas besoin de tous ces langages de programmation sophistiqués (niveau haut ou bas). Mais la vérité est que tout le monde a un niveau de confort différent quant à la proximité de la machine avec laquelle il veut écrire son code. Une fois que vous avez trouvé votre niveau, il vous suffit de vous en tenir à la syntaxe qui vous est donnée (ou d'écrire vos propres spécifications de langage et votre compilateur si vous êtes vraiment dérangé).
Hoki

12
+1 Une version plus concise pourrait être "Pourquoi toutes les langues naturelles distinguent-elles les noms des verbes?"
msw

2
"un blob incompréhensible de caractères est correct pour une machine à interpréter, mais il serait presque impossible pour les humains de comprendre et de conserver" Un qualificatif téméraire pour un changement syntaxique aussi modeste proposé dans la question. On s'adapte rapidement à la syntaxe. Cette proposition s'écarte beaucoup moins radicalement de la convention que la syntaxe objet de la méthode OO. La méthode (args) était à son époque, mais cette dernière ne s'est pas avérée impossible à comprendre et à maintenir pour les humains. Malgré le fait que dans la plupart des langages OO, les méthodes ne doivent pas être considérées comme stockées dans des instances de classe.
Marc van Leeuwen

4
@MarcvanLeeuwen. Votre commentaire est vrai et a du sens, mais la rédaction du message original provoque une certaine confusion. Il y a en fait 2 questions: (i) Pourquoi est-ce ainsi? et (ii) Pourquoi pas comme ça à la place? . La réponse de a whatsisnameété davantage une réponse au premier point (et une alerte sur le danger de supprimer ces sécurités), alors que votre commentaire est davantage lié à la seconde partie de la question. Il est en effet possible de changer cette syntaxe (et comme vous l'avez décrit, cela a déjà été fait à maintes reprises ...) mais cela ne conviendra pas à tout le monde (comme oop ne convient pas à tout le monde non plus.
Hoki

10

Vous serez peut-être intéressé d'apprendre que, dès la préhistoire, un langage appelé ALGOL 68 utilisait une syntaxe proche de celle que vous proposez. Reconnaissant que les identificateurs de fonction sont liés à des valeurs comme les autres identificateurs, vous pouvez dans ce langage déclarer une fonction (constante) à l'aide de la syntaxe

nom de type de fonction = ( liste de paramètres ) type de résultat : corps ;

Concrètement, votre exemple se lirait

PROC (INT)INT add one = (INT n) INT: n+1;

Reconnaître la redondance en ce que le type initial peut être lu à partir de la RHS de la déclaration, et comme un type de fonction commence toujours par PROC, cela pourrait (et le serait généralement) contracté avec

PROC add one = (INT n) INT: n+1;

mais notez que le =reste toujours avant la liste de paramètres. Notez également que si vous vouliez une variable de fonction (à laquelle une autre valeur du même type de fonction pourrait ultérieurement être affectée), elle =devrait être remplacée par :=, donnant l'une ou l'autre des options suivantes:

PROC (INT)INT func var := (INT n) INT: n+1;
PROC func var := (INT n) INT: n+1;

Cependant, dans ce cas, les deux formes sont en fait des abréviations; puisque l'identifiant func vardésigne une référence à une fonction générée localement, la forme complètement développée serait

REF PROC (INT)INT func var = LOC PROC (INT)INT := (INT n) INT: n+1;

Il est facile de s’habituer à cette forme syntaxique particulière, mais elle n’a manifestement pas eu un tel succès dans les autres langages de programmation. Même les langages de programmation fonctionnels comme Haskell préfèrent le style f n = n+1avec = suivant la liste des paramètres. Je suppose que la raison est principalement psychologique; après tout, même les mathématiciens ne préfèrent pas souvent, comme je le fais, f = nn + 1 sur f ( n ) = n + 1.

En passant, la discussion ci-dessus met en évidence une différence importante entre les variables et les fonctions: les définitions de fonction associent généralement un nom à une valeur de fonction spécifique, qui ne peut plus être modifiée, alors que les définitions de variable introduisent généralement un identifiant avec une valeur initiale , mais peut changer plus tard. (Ce n'est pas une règle absolue; des variables de fonction et des constantes non-fonction se produisent dans la plupart des langages.) De plus, dans les langages compilés, la valeur liée à une définition de fonction est généralement une constante de compilation, de sorte que les appels à la fonction peuvent être effectués. compilé en utilisant une adresse fixe dans le code. En C / C ++, c'est même une exigence. l'équivalent de l'ALGOL 68

PROC (REAL) REAL f = IF mood=sunny THEN sin ELSE cos FI;

ne peut pas être écrit en C ++ sans introduire un pointeur de fonction. Ce type de limitations spécifiques justifie l'utilisation d'une syntaxe différente pour les définitions de fonctions. Mais elles dépendent de la sémantique linguistique et la justification ne s’applique pas à toutes les langues.


1
"Les mathématiciens ne préfèrent pas f = nn + 1 sur f ( n ) = n + 1" ... Sans oublier les physiciens, qui aiment n'écrire que f = n + 1 ...
gauche du

1
@leftaroundabout c'est parce que ⟼ est difficile à écrire. Honnêtement.
Pierre Arlaud

8

Vous avez cité Java et Scala comme exemples. Cependant, vous avez oublié un fait important: ce ne sont pas des fonctions, ce sont des méthodes. Les méthodes et les fonctions sont fondamentalement différentes. Les fonctions sont des objets, les méthodes appartiennent à des objets.

Dans Scala, qui a à la fois des fonctions et des méthodes, il existe les différences suivantes entre les méthodes et les fonctions:

  • les méthodes peuvent être génériques, les fonctions ne peuvent pas
  • les méthodes peuvent n'avoir aucune, une ou plusieurs listes de paramètres, les fonctions ont toujours exactement une liste de paramètres
  • les méthodes peuvent avoir une liste de paramètres implicite, les fonctions ne peuvent pas
  • les méthodes peuvent avoir des paramètres optionnels avec des arguments par défaut, les fonctions ne peuvent pas
  • les méthodes peuvent avoir des paramètres répétés, les fonctions ne peuvent pas
  • les méthodes peuvent avoir des paramètres nommés, les fonctions ne peuvent pas
  • les méthodes peuvent être appelées avec des arguments nommés, les fonctions ne peuvent pas
  • les fonctions sont des objets, les méthodes ne sont pas

Donc, votre remplaçant proposé ne fonctionne tout simplement pas, du moins pour ces cas.


2
"Les fonctions sont des objets, les méthodes appartiennent à des objets." Est-ce que cela est censé s'appliquer à tous les langages (comme le C ++)?
svick

9
"les méthodes peuvent avoir des paramètres nommés, les fonctions ne peuvent pas" - ce n'est pas une différence fondamentale entre les méthodes et les fonctions, c'est juste un caprice de Scala. Même chose avec la plupart (tous?) Des autres.
user253751

1
Les méthodes ne sont fondamentalement qu'un type particulier de fonctions. -1. Notez que Java (et par extension Scala) n’a aucun autre type de fonction. Ce sont des "fonctions" qui sont des objets avec une méthode (en général, les fonctions ne peuvent pas être des objets ni même des valeurs de première classe; elles dépendent du langage).
Jan Hudec le

@JanHudec: Sémantiquement, Java a à la fois des fonctions et des méthodes; il utilise la terminologie "méthodes statiques" lorsqu'il fait référence à des fonctions, mais une telle terminologie n'efface pas la distinction sémantique.
Supercat

3

Les raisons auxquelles je peux penser sont:

  • Il est plus facile pour le compilateur de savoir ce que nous déclarons.
  • Il est important pour nous de savoir (de manière triviale) s'il s'agit d'une fonction ou d'une variable. Les fonctions sont généralement des boîtes noires et nous ne nous soucions pas de leur implémentation interne. Je n'aime pas l'inférence de type sur les types de retour dans Scala, car j'estime qu'il est plus facile d'utiliser une fonction ayant un type de retour: il s'agit souvent de la seule documentation fournie.
  • Et le plus important est la stratégie de foule utilisée dans la conception des langages de programmation. C ++ a été créé pour voler les programmeurs C, et Java a été conçu de manière à ne pas effrayer les programmeurs C ++, et C # pour attirer les programmeurs Java. Même C #, qui, à mon avis, est un langage très moderne avec une équipe incroyable derrière lui, a copié des erreurs de Java ou même de C.

2
"… Et C # pour attirer les programmeurs Java" - c'est discutable, C # ressemble beaucoup plus au C ++ qu'à Java. et parfois, il semble que cela ait été fait intentionnellement incompatible avec Java (pas de joker, pas de classe interne, pas de covariance de type de retour…)
Sarge Borsch

1
@SargeBorsch Je ne pense pas que cela ait été intentionnellement rendu incompatible avec Java, je suis presque sûr que l'équipe C # a simplement essayé de bien faire les choses, ou de les faire mieux, quel que soit le choix que vous envisagiez. Microsoft a déjà écrit ses propres Java et JVM et a été poursuivi. Donc, l'équipe C # était probablement très motivée pour éclipser Java. C'est sûrement incompatible, et c'est mieux. Java a commencé avec quelques limitations de base et je suis heureux que l'équipe C # ait choisi, par exemple, de faire les génériques différemment (visibles dans le système de typage et pas seulement avec Sugar).
codenheim

2

Si on ne cherche pas à modifier le code source sur une machine extrêmement contrainte en RAM, ou à minimiser le temps nécessaire pour le lire sur une disquette, quel est le problème avec l'utilisation de mots-clés?

Certes, il est plus agréable de lire x=y+zque store the value of y plus z into x, mais cela ne signifie pas que les caractères de ponctuation sont intrinsèquement "meilleurs" que les mots-clés. Si les variables i, jet ksont Integeret xsont Real, considérons les lignes suivantes en Pascal:

k := i div j;
x := i/j;

La première ligne effectuera une division entière tronquée, tandis que la seconde effectuera une division en nombre réel. La distinction peut être faite gentiment parce que Pascal utilise divcomme opérateur de division de entier entier tronqué, plutôt que d'essayer d'utiliser un signe de ponctuation qui a déjà un autre objectif (division de nombre réel).

Bien qu'il existe quelques contextes dans lesquels il peut être utile de rendre une définition de fonction concise (par exemple, un lambda utilisé dans le cadre d'une autre expression), les fonctions sont généralement supposées se distinguer et être facilement reconnaissables visuellement en tant que fonctions. Alors qu'il serait peut-être possible de faire la distinction beaucoup plus subtile et de n'utiliser que des caractères de ponctuation, à quoi servirait-il? Dire Function Foo(A,B: Integer; C: Real): Stringpermet de préciser le nom de la fonction, ses paramètres et ses résultats. Peut-être pourrait-on raccourcir le texte de six ou sept caractères en le remplaçant Functionpar des caractères de ponctuation, mais que gagnerait-il?

Une autre chose à noter est qu’il existe dans la plupart des cadres une différence fondamentale entre une déclaration qui associera toujours un nom à une méthode particulière ou à une liaison virtuelle particulière et une page qui crée une variable qui identifie initialement une méthode ou une liaison particulière, mais peut être modifié au moment de l' exécution pour en identifier un autre. Étant donné que ces concepts sont très sémantiquement très différents dans la plupart des cadres de procédures, il est logique qu’ils aient une syntaxe différente.


3
Je ne pense pas que cette question concerne la concision. D'autant que, par exemple, void f() {}est plus court que l'équivalent lambda en C ++ ( auto f = [](){};), C # ( Action f = () => {};) et Java ( Runnable f = () -> {};). La concision de lambdas provient de l'inférence de type et de l'omission return, mais je ne pense pas que cela soit lié à ce que cette question demande.
svick

2

La raison pourrait en être que ces langages ne sont pas assez fonctionnels, pour ainsi dire. En d'autres termes, vous définissez plutôt rarement des fonctions. Ainsi, l'utilisation d'un mot clé supplémentaire est acceptable.

Dans les langues de l'héritage ML ou Miranda, OTOH, vous définissez des fonctions la plupart du temps. Regardez un code Haskell, par exemple. C'est littéralement principalement une séquence de définitions de fonctions, beaucoup d'entre elles ont des fonctions locales et des fonctions locales de ces fonctions locales. Par conséquent, un mot-clé amusant en haaskell serait une erreur et nécessiterait une déclaration de détermination dans un langage impératif pour commencer avec assign . L'affectation de cause est probablement de loin la déclaration la plus fréquente.


1

Personnellement, je ne vois aucune faille fatale dans votre idée; vous trouverez peut-être plus délicat que vous ne le pensiez d'exprimer certaines choses à l'aide de votre nouvelle syntaxe et / ou vous devez peut-être le réviser (en ajoutant divers cas spéciaux et d'autres fonctionnalités, etc.), mais je doute que vous vous retrouviez vous-même. besoin d'abandonner entièrement l'idée.

La syntaxe que vous avez proposée ressemble plus ou moins à une variante de certains des styles de notation parfois utilisés pour exprimer des fonctions ou des types de fonctions en mathématiques. Cela signifie que, comme toutes les grammaires, il sera probablement plus attrayant pour certains programmeurs que pour d’autres. (En tant que mathématicien, j'aime bien.)

Cependant, il faut noter que dans la plupart des langues, la defsyntaxe de style (la syntaxe traditionnelle) ne se comporte différemment d'une affectation de variable standard.

  • Dans la famille Cet C++, les fonctions ne sont généralement pas traitées comme des "objets", c’est-à-dire des morceaux de données typées à copier et à mettre dans la pile, etc. (Oui, vous pouvez avoir des pointeurs de fonction, mais ceux-ci pointent toujours sur le code exécutable, pas sur "données" dans le sens typique.)
  • Dans la plupart des langages OO, il existe un traitement spécial pour les méthodes (c'est-à-dire les fonctions membres); c'est-à-dire qu'il ne s'agit pas simplement de fonctions déclarées dans le cadre d'une définition de classe. La différence la plus importante est que l'objet sur lequel la méthode est appelée est généralement transmis en tant que premier paramètre implicite à la méthode. Python rend ceci explicite avec self(qui, soit dit en passant, n'est pas réellement un mot-clé; vous pourriez faire de n'importe quel identifiant valide le premier argument d'une méthode).

Vous devez vous demander si votre nouvelle syntaxe représente avec précision (et intuitivement, idéalement) ce que le compilateur ou l'interpréteur est en train de faire. Il peut être utile de lire, par exemple, la différence entre lambdas et méthodes en Ruby; cela vous donnera une idée de la différence entre votre paradigme de fonctions - données - juste données et le paradigme OO / procédural typique.


1

Pour certaines langues, les fonctions ne sont pas des valeurs. Dans un tel langage pour dire que

val f = << i : int  ... >> ;

est une définition de fonction, alors que

val a = 1 ;

déclare une constante, est déroutant parce que vous utilisez une syntaxe pour signifier deux choses.

D'autres langages, tels que ML, Haskell et Scheme, traitent les fonctions comme des valeurs de première classe, mais fournissent à l'utilisateur une syntaxe spéciale pour déclarer des constantes à valeur de fonction. * Ils appliquent la règle selon laquelle "l'utilisation raccourcit la forme". Par exemple, si une construction est à la fois commune et prolixe, vous devez donner un raccourci à l'utilisateur. Il est inutile de donner à l'utilisateur deux syntaxes différentes qui signifient exactement la même chose; Parfois, l'élégance doit être sacrifiée à l'utilité.

Si, dans votre langue, les fonctions sont de 1ère classe, pourquoi ne pas essayer de trouver une syntaxe suffisamment concise pour ne pas être tenté de trouver un sucre syntaxique?

-- Modifier --

Un autre problème que personne n'a encore évoqué est la récursivité. Si vous permettez

{ 
    val f = << i : int ... g(i-1) ... >> ;
    val g = << j : int ... f(i-1) ... >> ;
    f(x)
}

et vous permettez

{
    val a = 42 ;
    val b = a + 1 ;
    a
} ,

est-ce qu'il s'ensuit que vous devriez autoriser

{
    val a = b + 1 ; 
    val b = a - 1 ;
    a
} ?

Dans un langage paresseux (comme Haskell), il n'y a pas de problème ici. Dans une langue ne contenant essentiellement pas de contrôles statiques (comme LISP), il n’ya pas de problème ici. Toutefois, dans un langage cohérent vérifié statiquement, vous devez faire attention à la définition des règles de contrôle statique si vous souhaitez autoriser les deux premiers et interdire les derniers.

- Fin de l'édition -

* On pourrait soutenir que Haskell ne fait pas partie de cette liste. Il fournit deux manières de déclarer une fonction, mais les deux sont, en un sens, des généralisations de la syntaxe pour déclarer des constantes d'autres types.


0

Cela peut être utile sur les langages dynamiques où le type n'est pas si important, mais ce n'est pas lisible dans les langages à typage statique où vous voulez toujours connaître le type de votre variable. De plus, dans les langages orientés objet, il est très important de connaître le type de votre variable afin de savoir quelles opérations sont prises en charge.

Dans votre cas, une fonction avec 4 variables serait:

(int, long, double, String => int) addOne = (x, y, z, s) => {
  return x + 1;
}

Quand je regarde l'en-tête de la fonction et que je vois (x, y, z, s) mais que je ne connais pas les types de ces variables. Si je veux savoir quel type zest le troisième paramètre, il faudra que je regarde le début de la fonction et commence à compter 1, 2, 3 et ensuite voir que le type est double. Dans le premier sens, je regarde directement et vois double z.


3
Bien sûr, il y a cette chose merveilleuse appelée inférence de type, qui vous permet d'écrire quelque chose comme var addOne = (int x, long y, double z, String s) => { x + 1 }dans un langage non moronique typé statiquement de votre choix (exemples: C #, C ++, Scala). Même l'inférence de type local par ailleurs très limitée utilisée par C # est tout à fait suffisante pour cela. Donc, cette réponse ne fait que critiquer une syntaxe spécifique qui est douteuse au départ et qui n'est réellement utilisée nulle part (bien que la syntaxe de Haskell pose un problème très similaire).
amon

4
@ amon Sa réponse peut être critiquer la syntaxe, mais il souligne également que la syntaxe proposée dans la question peut ne pas être "naturelle" pour tout le monde.

1
@amon L'utilité de l'inférence de type n'est pas une chose merveilleuse pour tout le monde. Si vous lisez du code plus souvent que vous écrivez du code, l'inférence de type est mauvaise car vous devez inférer dans votre tête le type lors de la lecture du code; et il est bien plus rapide de lire le type que de penser réellement au type utilisé.
m3th0dman

1
La critique me semble valable. Pour Java, le PO semble penser que [entrée, sortie, nom, entrée] est une fonction plus simple / plus claire que simplement [sortie, nom, entrée]? Comme @ m3th0dman le dit, avec des signatures plus longues, l'approche "plus propre" du PO devient très longue.
Michael,

0

Il existe une raison très simple d’avoir une telle distinction dans la plupart des langues: il est nécessaire de distinguer évaluation et déclaration . Votre exemple est bon: pourquoi ne pas aimer les variables? Eh bien, les expressions de variables sont immédiatement évaluées.

Haskell a un modèle spécial où il n'y a pas de distinction entre évaluation et déclaration, c'est pourquoi il n'est pas nécessaire d'utiliser un mot clé spécial.


2
Mais certaines syntaxes pour les fonctions lambda permettent également de résoudre ce problème, alors pourquoi ne pas l'utiliser?
svick

0

Les fonctions sont déclarées différemment des littéraux, des objets, etc. dans la plupart des langages car elles sont utilisées différemment, déboguées différemment et posent différentes sources d'erreur potentielles.

Si une référence d'objet dynamique ou un objet mutable est transmise à une fonction, celle-ci peut modifier la valeur de l'objet en cours d'exécution. Ce type d'effet secondaire peut rendre difficile la tâche de suivre le fonctionnement d'une fonction si elle est imbriquée dans une expression complexe. Il s'agit d'un problème courant dans les langages tels que C ++ et Java.

Envisagez de déboguer une sorte de module du noyau en Java, où chaque objet exécute une opération toString (). Bien que l'on puisse s'attendre à ce que la méthode toString () restaure l'objet, il peut être nécessaire de le désassembler et de le réassembler afin de convertir sa valeur en objet String. Si vous essayez de déboguer les méthodes que toString () appellera (dans un scénario de modèle) pour effectuer son travail et mettra accidentellement en évidence l'objet dans la fenêtre de variables de la plupart des IDE, le débogueur peut se bloquer. En effet, l'EDI va essayer de toString () l'objet qui appelle le code même que vous êtes en train de déboguer. Aucune valeur primitive ne fait jamais ce genre de foutaise, car la signification sémantique de ces valeurs est définie par le langage et non par le programmeur.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.