Pourquoi certains langages de programmation fonctionnels utilisent-ils un espace pour l'application de fonctions?

34

Après avoir examiné certains langages pour la programmation fonctionnelle, je me suis toujours demandé pourquoi certaines langues fp utilisaient un ou plusieurs caractères d'espacement pour l'application (et la définition) de fonctions, alors que la plupart (tous?) Les langages à impératif / orienté utilisent des parenthèses être le moyen le plus mathématique. Je pense aussi que ce dernier style est beaucoup plus clair et lisible que sans les parens.

Donc, si nous avons une fonction f (x) = x², il existe deux alternatives pour l'appeler:

  • FP: f x

    Exemples:

    • ML, Ocaml, F #
    • Haskell
    • LISP, Scheme (en quelque sorte)
  • Non-FP: f(x)

    Exemples:

    • Presque toutes les langues impératives (je sais, voir les commentaires / réponses)
    • Erlang
    • Scala (permet également la "notation d'opérateur" pour les arguments simples)

Quelles sont les raisons pour "omettre" les parenthèses?

pvorb
la source
9
Pour rendre cela plus confus, euh, je voulais dire succinct .
Den
11
@ Den si vous apprenez haskell, la syntaxe sera l'une des choses les moins déroutantes: p
Simon Bergot
5
Vous réalisez l'ironie de dire que l'utilisation de parenthèses serait plus mathématique et d'utiliser ensuite la fonction d'exponentiation comme exemple?
Jörg W Mittag
12
@ Den, vous vous faites du mal en rejetant une langue à cause de sa syntaxe. Vous n’avez sûrement pas eu de difficulté à apprendre xml ou sql (ce ne sont pas des langages à usage général, mais ils définissent leur propre syntaxe).
Simon Bergot
7
Je voudrais souligner que les scripts shell (par exemple, bash) n'utilisent généralement pas de paramètres pour appeler des commandes. PowerShell a le pire des deux mondes en ce que les fonctions sont déclarées avec des parenthèses et appelées sans elles.
Kris Harper

Réponses:

59

qui semble être le moyen le plus mathématique

les langages fonctionnels sont inspirés du lambda calcul . Dans ce champ, les parenthèses ne sont pas utilisées pour l’application de fonctions.

Je pense aussi que ce dernier style est beaucoup plus clair et lisible que sans les parens.

La lisibilité est dans l'oeil du spectateur. Vous n'êtes pas habitué à le lire. C'est un peu comme les opérateurs mathématiques. Si vous comprenez l'associativité, il vous suffit de quelques paragraphes pour clarifier la structure de votre expression. Souvent, vous n'en avez pas besoin.

Le curry est aussi une bonne raison d’utiliser cette convention. Dans haskell, vous pouvez définir les éléments suivants:

add :: Int -> Int -> Int
add x y = x + y

x = add 5 6 -- x == 11
f = add 5
y = f 6 -- y == 11
z = ((add 5) 6) -- explicit parentheses; z == 11

Avec parens, vous pouvez utiliser deux conventions: f(5, 6)(non curry) ou f(5)(6)(curry). La syntaxe haskell aide à s’habituer au concept de currying. Vous pouvez toujours utiliser une version sans curry, mais il est plus pénible de l'utiliser avec des combinateurs.

add' :: (Int, Int) -> Int
add' (x, y) = x + y
u = add'(5, 6) -- just like other languages
l = [1, 2, 3]
l1 = map (add 5) l -- [6, 7, 8]
l2 = map (\x -> add'(5, x)) l -- like other languages

Remarquez comment la deuxième version vous oblige à enregistrer x en tant que variable et que la sous-expression est une fonction qui prend un entier et lui ajoute 5? La version au curry est beaucoup plus légère, mais est également considérée par beaucoup comme plus lisible.

Les programmes Haskell utilisent beaucoup les applications partielles et les combinateurs pour définir et composer les abstractions. Ce n’est donc pas un exemple. Une bonne interface fonctionnelle sera celle où l'ordre des paramètres fournit une utilisation conviviale au curry.

Autre point: une fonction sans paramètre doit être appelée avec f(). Dans haskell, puisque vous ne manipulez que des valeurs évaluées immuables, vous écrivez fet vous le considérez comme une valeur qui devra effectuer des calculs si nécessaire. Étant donné que son évaluation n'aura aucun effet secondaire, il ne sert à rien d'avoir une notation différente pour la fonction sans paramètre et sa valeur renvoyée.

Il existe également d'autres conventions pour l'application de fonctions:

  • Lisp: (fx) - préfixe avec des parenthèses externes
  • Pour: xf - postfix
Simon Bergot
la source
7
Nitpick mineur: les termes sont "currying" et "currys", pas "currification" et "currified".
Doval
"non au curry" qu'est-ce qu'une fonction non au curry dans haskell?
Ven.
3
@ user1737909 Une fonction qui prend un tuple en argument.
Doval
1
@Doval En fait, cela devrait être “schönfinkeling” et “schönfinkeled”. Mais bon, l’histoire définit toujours le vainqueur rétrospectivement.
Profpatsch
Penser à une syntaxe corroyage parenthésée, quelque chose comme f(a, ,b)ou add(a, )ou add(, b)semble plus souple par défaut.
phresnel
25

L'idée de base est de rendre l'opération la plus importante (application fonctionnelle) la plus facile à lire et à écrire. Un espace est très peu intrusif à lire et très facile à taper.

Notez que cela n’est pas spécifique aux langages fonctionnels, par exemple en Smalltalk , l’un des premiers langages OO et inspirés par lui ( Self , Newspeak , Objective-C), mais aussi en Io et les langages qu’il inspire (Ioke, Seph), Les espaces sont utilisés pour les appels de méthodes.

Petit-style:

anArray sort
anArray add: 2
aString replace: "a" with: "b"

(Dans ce dernier cas, le nom de la méthode est replace:with:.)

Io-style:

anArray sort
anArray add(2)
aString replace("a", "b")

Scala autorise également les espaces blancs pour les appels de méthodes:

foo bar(baz)

Et en laissant les parenthèses s'il n'y a qu'un seul argument:

foo bar baz

Ruby vous permet également de ne pas utiliser les parenthèses:

an_array.sort
an_array.add 2
a_string.replace "a", "b"

en utilisant des parenthèses, ce qui semble être le moyen le plus mathématique

Pas vraiment:

f(x)
sin x
x²
|x|
x!
x + y
xy
½

La notation mathématique a évolué sur une longue période d'une manière horriblement incohérente.

Le cas échéant, les langages fonctionnels s’inspirent du λ-calcul où l’application de fonctions est écrite en utilisant des espaces.

Jörg W Mittag
la source
3
Il y a aussi un argument d'économie de montage. En particulier, les langages fonctionnels prennent généralement en charge la composition directement. Mettre des parenthèses autour de la fonction au lieu de l'argument a du sens. Il suffit de le faire "une fois": (e. F. G) x par opposition à e (f (g (x))). En outre, Haskell, au moins, n’empêcherait pas un de faire f (x) au lieu de f x. Ce n'est tout simplement pas idiomatique.
nomen
18

La parenthèse pour l’application de fonctions n’est que l’une des nombreuses selles que nous a laissées Euler. Comme toute autre chose, les mathématiques ont besoin de conventions quand il y a plusieurs façons de faire quelque chose. Si votre éducation mathématique ne se limite pas à une matière non mathématique à l'université, alors vous n'êtes probablement pas trop familiarisé avec les nombreux domaines dans lesquels l'application d'une fonction se déroule sans problème, sans aucune de ces parenthèses sans signification (par exemple, la géométrie différentielle, l'algèbre abstraite). Et vous ne mentionnez même pas les fonctions appliquées infix (presque toutes les langues), "outfix" comme prendre une norme, ou sous forme de diagramme (Befunge, Topologie algébrique).

Donc, en réponse, je dirais que cela est dû à une proportion beaucoup plus élevée de programmeurs fonctionnels et de concepteurs de langage ayant une formation approfondie en mathématiques. Von Neumann dit: "Jeune homme, en mathématiques, vous ne comprenez pas les choses. Vous vous y habituez.", C'est certainement vrai en notation.

Sean D
la source
7
Ne me lancez pas sur f(x)vs sin xvs vs |x|vs x!vs x + yvs xyvs ½vs une sommation par rapport à un rapport intégral ...
Jörg W Mittag
2
+1 pour la citation de von Neuman. +1 pour le reste de la réponse, mais je ne peux pas donner +2. :(
pvorb
2
J'attrape un soupçon d'élitisme ici; Je conteste la neutralité de "les concepteurs de langages de programmation fonctionnels sont mieux éduqués" .
CaptainCodeman
7
@CaptainCodeman Il dit plus éduqué en mathématiques, une déclaration avec laquelle je suis d'accord. Au moins en ce qui concerne Haskell, vous remarquerez que les deux concepts sont de nature plus mathématique et que la communauté est également plus encline à la mathématique. Ils ne sont pas plus intelligents, mais simplement mieux éduqués en mathématiques.
Paul
5
@ CaptainCodeman Non, puisqu'il n'a jamais laissé entendre qu'ils étaient plus éduqués en général, mais plus éduqués en mathématiques. C'est exactement ce qu'il a dit: "éducation mathématique approfondie". :)
Paul
8

Bien qu'il y ait beaucoup de vérité dans la réponse de Simon, je pense qu'il y a aussi une raison beaucoup plus pratique. La nature de la programmation fonctionnelle a tendance à générer beaucoup plus de parenthèses que la programmation impérative, en raison de l'enchaînement et de la composition des fonctions. Il arrive également que ces modèles d’enchaînement et de composition puissent être représentés sans ambiguïté sans parenthèses.

En bout de ligne, toutes ces parenthèses deviennent ennuyeuses à lire et à suivre. C'est probablement la principale raison pour laquelle LISP n'est pas plus populaire. Donc, si vous pouvez obtenir le pouvoir de la programmation fonctionnelle sans le désagrément de parenthèses en excès, je pense que les concepteurs de langage vont avoir tendance à adopter cette méthode. Après tout, les concepteurs de langues sont aussi des utilisateurs de langues.

Même Scala permet au programmeur d'omettre les parenthèses dans certaines circonstances, ce qui arrive assez souvent lors de la programmation dans un style fonctionnel, ce qui vous permet d'obtenir le meilleur des deux mondes. Par exemple:

val message = line split "," map (_.toByte)

Les parens à la fin sont nécessaires à l’associativité, les autres sont laissés de côté (ainsi que les points). L’écrire de cette manière souligne la nature enchaînée des opérations que vous effectuez. Cela n’est peut-être pas familier pour un programmeur impératif, mais pour un programmeur fonctionnel, écrire de cette façon est très naturel et fluide, sans avoir à s’arrêter pour insérer une syntaxe qui n’ajoute aucune signification au programme, mais est juste là pour rendre le compilateur heureux. .

Karl Bielefeldt
la source
2
"En fin de compte, toutes ces parenthèses deviennent ennuyeuses à lire et à suivre. C'est probablement la principale raison pour laquelle LISP n'est pas plus populaire.": Je ne pense pas que les parenthèses soient une raison pour que Lisp ne soit pas populaire: je ne pense pas ils sont particulièrement difficiles à lire, et je trouve que la syntaxe est beaucoup plus compliquée dans d’autres langues.
Giorgio
2
La syntaxe LISP compense largement les ennuyeuses parenthèses avec son très haut degré d'orthogonalité. Cela signifie simplement qu'il a d'autres fonctionnalités de rachat qui le rendent toujours utilisable, pas que les parens ne soient pas ennuyeux. Je comprends que ce ne soit pas tout le monde qui le pense, mais la grande majorité des programmeurs que j'ai rencontrés le font.
Karl Bielefeldt
"Lost In Stupid Parentheses" est mon backronym LISP préféré. Les ordinateurs peuvent également utiliser le moyen humain de spécifier des blocs de code et de supprimer la redondance, comme dans des langues telles que Wisp .
Cees Timmerman
4

Les deux prémisses sont fausses.

  • Ces langages fonctionnels n'utilisent pas d' espace pour l'application de fonction. Ce qu'ils font, c'est simplement analyser toute expression qui suit une fonction en tant qu'argument.

    GHCi> f 3
    4
    GHCi> f (3)
    4
    GHCi> (f) 3
    4

    Bien sûr, si vous n'utilisez ni espace ni parenthèse, cela ne fonctionnera normalement pas, tout simplement parce que l'expression n'est pas correctement symbolisée.

    GHCi> f3

    <‌interactif>: 7: 1:
        Pas dans la portée: 'f3'
        Vous vouliez peut-être dire 'f' (ligne 2)

    Mais si ces langues étaient limitées à des noms de variable à un caractère, cela fonctionnerait aussi.

  • Les conventions mathématiques n'exigent pas non plus normalement de parenthèses pour l'application de la fonction. Non seulement les mathématiciens écrivent souvent sin x - pour les fonctions linéaires (généralement appelés opérateurs linéaires), il est également très courant d'écrire l'argument sans parenthèses, peut-être parce que (comme toute fonction dans la programmation fonctionnelle) les fonctions linéaires sont souvent gérées sans fournir directement une dispute.

Votre question se résume donc: pourquoi certaines langues nécessitent-elles des parenthèses pour l’application de fonctions? Eh bien, apparemment, certaines personnes, comme vous, considèrent que ceci est plus lisible. Je suis tout à fait en désaccord avec cela: une paire de parens est agréable, mais dès que vous en avez plus de deux imbriquées, cela commence à devenir confus. Le code Lisp le montre bien, et pratiquement tous les langages fonctionnels sembleraient être encombrés de la même manière si vous aviez besoin de parens partout. Cela ne se produit pas tellement dans les langues impératives, mais principalement parce que ces langues ne sont pas assez expressives pour écrire des lignes simples concises et claires en premier lieu.

à gauche autour de
la source
Got me, la question n'est pas parfaite. :) Donc, la conclusion à tirer est: "Les parenthèses ne sont pas proportionnelles."
Pvorb
2
Ne sont des langues non fonctionnelles qui unanimes à exiger entre parenthèses; pour quelques exemples, Smalltalk object method: args, Objective C [object method: args], et Ruby et Perl autorisent l'omission de parenthèses dans les appels de fonctions et de méthodes, les obligeant uniquement à résoudre les ambiguïtés.
Hobbs
1

Une petite clarification historique: la notation originale en mathématiques était sans parenthèse !

La notation fx pour une fonction arbitraire de x a été introduite par Johan Bernoulli vers 1700, peu après que Leibniz ait commencé à parler de fonctions (en réalité, Bernoulli a écrit x ). Mais auparavant, beaucoup de gens utilisaient déjà des notations telles que sin x ou lx (le logarithme de x ) pour des fonctions spécifiques de x , sans parenthèse. Je suppose que c'est la raison pour laquelle beaucoup de gens écrivent encore aujourd'hui péché x au lieu de péché (x) .

Euler, qui était élève de Bernoulli, adopta le x de Bernoulli et transforma le grec en lettre latine, en écrivant fx . Plus tard, il remarqua qu'il était peut-être facile de confondre f pour une "quantité" et croyait que fx était un produit, aussi commença-t-il en écrivant f: x . Par conséquent, la notation f (x) n'est pas due à Euler. Bien sûr, Euler avait besoin de parenthèses pour la même raison que nous avons toujours besoin de parenthèses dans les langages de programmation fonctionnels: pour distinguer par exemple (fx) + y de f (x + y) . Je suppose que les personnes après Euler ont adopté la parenthèse comme valeur par défaut car elles ont principalement vu Euler écrire des expressions composées comme f (ax + b). Il a écrit que rarement fx .

Pour plus d'informations à ce sujet, voyez- vous : Euler a-t-il déjà écrit f (x) avec des parenthèses? .

Je ne sais pas si les concepteurs de langages de programmation fonctionnels ou les inventeurs du lambda calcul étaient conscients des racines historiques de leur notation. Personnellement, je trouve la notation sans parenthèse plus naturelle.

Michael Bächtold
la source