La syntaxe en double pour définir les fonctions nommées est-elle une mauvaise décision de conception de langage?

9

Je modélise un langage de programmation pour le plaisir, et la syntaxe est fortement influencée par Scala - en particulier les définitions de fonction.

J'ai rencontré un problème de conception car mon langage ne fait pas de distinction entre les fonctions définies via la defsyntaxe (méthodes de classe) et les fonctions anonymes affectées aux valeurs (créées à l'aide =>) - il supprime les différences d' implémentation et de comportement .

Le résultat est que les deux définitions suivantes signifient la même chose:

def square(x: Int) = x*x

val square = (x: Int) => x*x

Il n'y a aucune raison d'utiliser ce dernier formulaire (affectation de fonction anonyme immédiate) dans toute situation normale - il est simplement possible de l'utiliser à la place du defformulaire.

Une telle syntaxe en double pour définir des fonctions nommées nuirait-elle à l'orthogonalité du langage ou à un autre aspect de la conception?

Je préfère cette solution car elle permet des définitions courtes et intuitives des méthodes et des fonctions nommées (via def), et des définitions courtes des fonctions anonymes (via =>).

Edit: Scala fait la différence entre les deux - les fonctions anonymes ne sont pas les mêmes que les méthodes définies avec defScala. Les différences sont cependant relativement subtiles - voir les articles que j'ai liés auparavant.

jcora
la source
However, assigning existing functionssemble manquer la fin de la phrase
Izkata
1
Pouvez-vous définir des fonctions récursives en utilisant votre valnotation?
Giorgio
2
J'ai vérifié que cela est également possible à Scala. En SML, ce n'est pas le cas et vous devez utiliser funpour définir une fonction récursive.
Giorgio
3
La deuxième forme n'est pas vraiment une structure syntaxique spéciale, en deffait. C'est juste un effet secondaire du fait qu'une fonction anonyme, disons (x : Int) => x + 1est un objet, et que les objets peuvent être affectés à des valeurs avec val f = .... Les concepteurs de langage auraient dû faire tout leur possible pour interdire la syntaxe. Ce n'est pas tout à fait la même chose que de faire explicitement l'effort de prendre en charge deux syntaxes différentes qui font (approximativement) la même chose.
KChaloux
3
L'avantage majeur de faire quelque chose de plus d'une façon dans une langue est que c'est un excellent moyen d'entamer des débats religieux improductifs qui distraient des vrais problèmes (Penser C ++ ici) .......
mattnz

Réponses:

3

Je pense qu'avoir deux constructions qui signifient la même chose mais qui semblent différentes devrait être réduit au strict minimum dans une langue. Toute duplication augmente la difficulté de lire (et donc d'écrire / modifier du code dans) votre langue. L'élimination de toute duplication est inévitable dans un langage qui peut créer des constructions arbitraires (par exemple, l'équivalence de l'itération contre la récursivité).

Donc, dans ce cas, je pense qu'il pourrait être mieux conçu ici. Une seule façon de définir les fonctions est la plus logique pour moi. Dans ce cas, il semble que les deux déclarations scala que vous avez aient des implications légèrement différentes, ce qui n'est probablement pas une bonne conception (il est probablement préférable d'avoir quelque chose de clair qui indique quelles sont les différences, comme un mot-clé).

En fait, vous pouvez appliquer ce principe non seulement aux fonctions nommées, mais à n'importe quelle fonction. Pourquoi y a-t-il une différence dans la définition des fonctions nommées et des fonctions anonymes? Dans Lima , les fonctions sont toujours définies comme suit: fn[<arguments>: <statements>]. Si vous voulez qu'il soit « nommé » vous pouvez l' assigner à une variable: var x = fn[<arguments: <statements>]et si vous voulez passer dans une autre fonction anonyme: function[fn[<arguments: <statements>]]. Si vous voulez qu'il soit hissé, rendez-le constant const var x = fn[<arguments: <statements>]. La forme unique rend évident qu'ils signifient la même chose.

BT
la source
C'est assez intéressant qui constprovoque le levage, mais c'est parfaitement logique. Dans JS function myFunc, le levage entraîne, mais var myFunc =pas, ce qui est peut-être un peu moins intuitif car ils se comportent à peu près de la même manière sinon.
mpen
1
@mpen Oui, en fait, javascript fait essentiellement la même chose. la function fnName()...forme crée en fait une constante, ce qui fait que le levage est une chose valable à faire avec. Javascript rend les choses assez déroutantes lorsque vous utilisez le formulaire, var fn = function anotherFnName()...car cela rend le nom anotherFnName non palpable, même s'il est clairement constant.
BT
2

Ce que vous avez publié est une scala valide et fonctionne très bien.

Étant donné que le doublement n'a pas causé de problèmes avec scala (à ma connaissance), je vais dire que ce ne sera pas un problème pour votre langue non plus.

Daenyth
la source
1
Il est valide dans Scala, il fonctionne principalement de la même manière, mais cela ne signifie pas la même chose - et si vous faites des exemples plus complexes (le polymorphisme de type n'est pas disponible pour les fonctions anonymes par exemple) - les différences deviendraient plus apparentes.
jcora
2

J'ai trouvé une différence fondamentale entre les lambdas et les defméthodes dans Scala - que je ne suis toujours pas sûr de vouloir implémenter. Je dois faire d'autres recherches à ce sujet, puis je rendrai compte de ma décision.

Essentiellement, seules les méthodes peuvent return- et lorsque le mot-clé est utilisé à partir d'un lambda, il revient en fait à partir de la méthode englobante.

Comme je l'ai dit, je ne suis pas sûr de vouloir cela. Mais cela pourrait suffire à justifier cette syntaxe. Ou peut-être trop dangereux car des différences subtiles peuvent inopinément nuire.

Détails

jcora
la source