Fonctions de première classe

11

J'ai commencé à jeter un œil sérieux à Lisp ce week-end (j'entends par là que je n'ai appris que Lisp et que je ne suis pas revenu à des projets en C #) et je dois dire que j'adore ça. J'ai essayé d'autres langages fonctionnels (F #, Haskell, Erlang) mais je n'ai pas ressenti le tirage que Lisp m'a donné.

Maintenant que je continue d'apprendre Lisp, j'ai commencé à me demander pourquoi les langages non fonctionnels ne prennent pas en charge les fonctions de première classe. Je sais que des langages tels que C # peuvent faire des choses similaires avec les délégués et dans une certaine mesure, vous pouvez utiliser des pointeurs vers des fonctions en C / C ++, mais y a-t-il une raison pour laquelle cela ne deviendrait jamais une fonctionnalité dans ces langages? Y a-t-il un inconvénient à rendre les fonctions de première classe? Pour moi, c'est extrêmement utile, donc je ne sais pas pourquoi plus de langues (en dehors du paradigme fonctionnel) ne l'implémentent pas.

[Modifier] J'apprécie les réponses jusqu'à présent. Puisqu'on m'a montré que de nombreuses langues prennent désormais en charge des fonctions de première classe, je reformulerai la question: pourquoi faudrait-il autant de temps aux langues pour les implémenter? [/Éditer]

Jetti
la source
3
Je ne suis pas d'accord que C # peut faire "des choses similaires". Je dirais qu'il a des fonctions de première classe à toutes fins utiles (ce qui ruine votre prémisse). Il a une syntaxe pour les littéraux de fonction, vous pouvez bourrer les fonctions en variables, etc.
Logan Capaldo
@Logan - Je débattais pour savoir si l'inclusion d'exemples C # mais j'ai décidé de me baser sur cette entrée wikipedia . Selon l'article, il n'y a pas de véritables fonctions de première classe car "puisque les objets qui contiennent l'état dynamique de la fonction doivent être construits manuellement". Cependant, si c'est une déclaration incorrecte, j'aimerais savoir pourquoi! (Après tout, je suis ici pour apprendre et ne pas être coincé dans mes voies!)
Jetti
2
cette entrée wikipedia est obsolète. Un C # moderne fournit un lambda approprié.
SK-logic
Je pense que ce paragraphe est mal écrit. Il semble que les foncteurs C ++ ne sont pas de première classe, et les délégués C # non plus? Bien que je puisse être d'accord avec l'argument des foncteurs, je ne suis pas sûr que je le ferais pour les délégués C #. Il dit également "(voir lambda lifting)", qui est une déclaration sur ces langages supportant les fermetures lexicales plutôt que "fonctionne comme des valeurs". Vous pouvez en avoir un sans l'autre. Même si c'est le cas, ce n'est pas vrai pour C #, il a des fermetures lexicales. Une partie du problème est que la «fonction de première classe» peut être un terme vague.
Logan Capaldo
1
@Logan & SK-Logic - J'apprécie vos commentaires et je reste constructif. J'apprends encore et c'est très agréable de ne pas être flambé, donc j'apprécie ça. Merci beaucoup pour les commentaires et réponses!
Jetti

Réponses:

5

C #, VB.NET, Python, JavaScript, maintenant même C ++ 0x fournit des fonctions de première classe.

Mettre à jour:

Une implémentation de fermetures nécessite un lambda-lifting - une technique à l'origine assez étrangère aux programmeurs impératifs. Les fonctions de première classe appropriées (qui incluent les fermetures) nécessitent au moins une certaine prise en charge par le runtime et la machine virtuelle. Il a également fallu un certain temps pour intégrer les anciennes techniques fonctionnelles dans le monde impératif.

Et, la partie la plus importante - les fonctions de première classe sont à peine utilisables s'il n'y a pas de récupération de place. Il n'a été introduit dans une programmation de masse que récemment, avec Java et, par conséquent, .NET. Et l'approche Java originale consistait à simplifier à l'excès le langage afin de le rendre compréhensible par les codeurs moyens. .NET a d'abord suivi et s'est récemment séparé de cette direction (à mon humble avis, tout à fait justifiée et appropriée).

Tous ces concepts prennent du temps à être digérés par l'industrie.

SK-logic
la source
J'ai modifié la question d'origine en fonction des réponses.
Jetti
Fonctions de première classe! = Fermetures (et GC est beaucoup plus nécessaire pour ces dernières). Bien que, oui, ils soient étroitement liés et vous les voyez rarement, voire jamais, séparés.
@delnan, sans au moins une certaine forme de fermetures lexicales, les fonctions ne sont ni composables ni constructibles au moment de l'exécution, ce qui est obligatoire pour une définition de fonctions de première classe.
SK-logic
Non. Vous pouvez interdire de faire référence aux variables des étendues englobantes (ou copier les valeurs de la variable référencée au moment de la création de la fonction - ne sais pas si cela compte comme fermeture). Le résultat n'est pas particulièrement utile, mais les fonctions peuvent toujours être construites au moment de l'exécution sans être des fermetures et vous auriez donc des fonctions de première classe (étranges).
1
@ SK-logic: C ++ 0x fournit des fermetures sans garbage collection avec l'astuce habituelle -> si la variable sort du cadre et que vous vous accrochez toujours à une référence, l'utilisation de la référence est un comportement non défini. Il est donc possible ... cela met juste le honus sur le développeur.
Matthieu M.
5

Les fonctions de première classe sont beaucoup moins intéressantes sans fermetures.

Les fermetures ne sont pas vraiment possibles sans une sorte de gestion dynamique de la portée (garbage collection). L'une des choses qui rend C / C ++ si performant est le fait qu'il est beaucoup plus facile de compiler un langage dans lequel vous gérez la mémoire manuellement, de sorte que le C / C ++ est retiré de l'équation.

Et puis oui, il y a la question de l'impact de la POO. Vous n'avez plus de séparation des méthodes et des propriétés. Les méthodes sont elles-mêmes des propriétés qui acceptent les fonctions comme valeurs. Dans ce contexte, qu'est-ce que cela signifie vraiment de surcharger les méthodes puisque vous pouvez simplement échanger les choses idiotes à tout moment. Pouvez-vous vraiment ajouter cela à Java, par exemple, sans introduire un changement de paradigme majeur pour l'ensemble du langage? Où est le contrat ou ce sentiment de sécurité (faux OMI) que les gens connaissent en sachant que la construction garantit qu'elle fonctionnera toujours de la même manière à chaque fois? Il pourrait être judicieux de traiter les fonctions liées aux objets comme des méthodes comme un organisme différent dans les langages qui permettaient aux fonctions d'exister indépendamment en premier lieu, mais en Java, ce n'est pas vraiment une option.

En JavaScript, la POO et la fonctionnalité sont très imbriquées et je trouverais personnellement difficile de voir les fonctions de première classe comme tout sauf boulonnées dans des langues où elles ne l'étaient pas. Mais cela nécessite un certain nombre d'autres changements de changement de paradigme, notamment des niveaux élevés de mutabilité dans les objets et leurs méthodes eux-mêmes, ainsi que la possibilité d'appeler des fonctions comme s'ils étaient membres d'un objet à la volée.

Les langues ne sont pas seulement des ensembles d'outils remplis de do-hickeys pour faire avancer les choses pour la plupart. Ce sont des paradigmes. Des bundles d'idées, de stratégies et d'opinions qui vont de pair, d'une manière qui est connectée (ou semble connectée). Dans de nombreux cas, les fonctions de nom et de verbe ne correspondent pas du tout au paradigme ou, au mieux, sont un ajustement imparfait.

Cela dit, j'ai l'impression de manquer un bras lorsque je suis obligé de coder sans eux.

Erik Reppen
la source
Vous pouvez soit traiter toutes les méthodes comme scellées, soit simplement sceller celles qui vous intéressent le plus, et tout ira bien.
Alexei Averchenko
@AlexeiAverchenko Je suis ravi de dire que je ne suis pas sûr à 100% de ce que vous entendez par là. Désolé d'avoir raté votre commentaire jusqu'à maintenant. Googler «méthodes scellées».
Erik Reppen
4

Ce n'est pas vraiment impossible. Mais c'est encore une autre fonctionnalité, et le budget de complexité est limité. Il peut être considéré comme inutile par (ou ne vient même pas à l'esprit) par le concepteur de langage - "pourquoi diable aurions-nous besoin de passer des fonctions? Ce langage est destiné à la programmation d'un système d'exploitation!". La fusion avec le reste de la langue peut également demander des efforts importants. Par exemple, un type de fonction peut nécessiter de sérieux ajouts au système de type (par exemple en Java), encore plus si vous avez une surcharge (merci @Renesis de l'avoir signalé). Vous devez également fournir du code de bibliothèque pour utiliser le viable - personne ne veut se définir map.

Cela pourrait également rendre d'autres objectifs de la langue plus difficiles à atteindre:

  • Il devient plus difficile à optimiser (pas vraiment plus difficile dans de nombreux cas, mais nécessite des approches différentes de celles auxquelles vous êtes habitué). Par exemple, si des fonctions globales peuvent être réaffectées, vous devez prouver qu'elles fne sont jamais réaffectées avant de les aligner.
  • Dans le même ordre d'idées, si vous créez des fonctions entièrement fonctionnelles, vous avez besoin d'un compilateur intelligent et de JIT pour supprimer la surcharge associée à cela et rendre l'appel aussi efficace (en termes de vitesse et d' espace) que de pousser des arguments et de renvoyer une adresse et de sauter vers une adresse.
  • Comme déjà mentionné, c'est une autre fonctionnalité complète et les premières étapes vers la prise en charge d'un autre paradigme - si vous voulez un langage simple, vous ne pouvez peut-être pas vous permettre d'ajouter des fonctions de première classe sans lancer d'autres choses (que vous pouvez considérer comme beaucoup plus importantes) en dehors.
  • Peut-être que cela ne se mélange tout simplement pas bien avec le reste de la langue. Si vous concevez le langage comme, par exemple, la meilleure incarnation de la POO depuis Smalltalk, vous voudrez peut-être vous concentrer sur cela au lieu d'offrir un autre paradigme très différent. Surtout s'il est difficile d'intégrer les deux (voir ci-dessus). N paradigmes bien faits est plus agréable à programmer que N + 1 paradigmes mal faits.

Dans le même ordre d'idées, on peut se demander pourquoi un langage de programmation L1 n'a pas la caractéristique F du langage très différent L2.


la source
1

Je pense que le premier problème est un malentendu selon lequel Orienté Objet et Fonctionnel ne peuvent pas être mélangés. Une liste rapide des langages décrits, au moins par Wikipedia, comme les deux: JavaScript , ActionScript , Python , C # , F # .

Le deuxième problème semble être une définition des fonctions de première classe - pourquoi ne considérez-vous pas que C # en a, par exemple?

Je ne suis pas un expert en langage fonctionnel, veuillez donc me corriger dans les commentaires si je me trompe, mais je crois comprendre que l'inclusion de fonctions de première classe elle-même définit plus ou moins si un langage prend en charge un paradigme fonctionnel .

Quelle est la vraie question?

Donc, comprendre que les langages orientés objet peuvent également être fonctionnels et que ces langages contiennent des fonctions de première classe, il semble que la question que vous cherchez est pourquoi certains langages orientés objet ne contiennent-ils pas de fonctions de première classe?

Surcharge de fonction

L'une des principales raisons à cela est la difficulté de définir des fonctions de première classe lorsque le langage a également choisi de prendre en charge la surcharge de fonctions (exemple en Java):

public int dispatchEvent(Event event);
public int dispatchEvent(String event, Object data);

À quelle dispatchEventfonction une variable ferait-elle référence?

Programmation par classe

La programmation basée sur les classes n'empêche pas un langage de prendre en charge des objets de première classe, mais elle peut rendre les choses plus confuses ou ajouter des questions auxquelles il faut répondre.

ActionScript, par exemple, en tant que langage ECMAScript a commencé dans un paradigme beaucoup plus fonctionnel comme JavaScript. Les fonctions n'étaient pas intrinsèquement liées aux objets, elles pouvaient essentiellement être appelées avec n'importe quel objet car elles sont de portée au moment de l'exécution.

Lorsque vous ajoutez des classes (non dynamiques), vous avez des fonctions qui sont intrinsèquement liées à une instance, pas seulement la classe elle-même. Cela jette quelques rides dans la spécification de ce Function.applyque je n'ai honnêtement pas creusé pour savoir comment ils l'ont traité.

Nicole
la source
3
"L'inclusion de fonctions de première classe elle-même rend plus ou moins un langage fonctionnel." - Faux. C'est un pré-requis, oui. Mais il y a beaucoup plus, comme (pour n'en nommer que trois) éviter l'état mutable, l'accent mis sur les expressions et l'application de fonction plutôt que sur les instructions séquentielles et l'accent mis sur la transparence référentielle.
@delnan Vous devriez aller corriger Wikipedia alors ... A moins qu'un "langage fonctionnel" et un "langage supportant un paradigme fonctionnel" soient deux choses différentes.
Nicole
1
@Rensis: Ce sont des choses différentes. Vous pouvez créer quelque chose comme des objets (y compris même des vtables) en C, mais ce n'est pas joli. Passer de "possède des fonctions de première classe" à "est une langue fonctionnelle" n'est pas si mal, mais c'est quand même une différence. De plus, le wiki indique (correctement) sur la page que vous avez liée à "Ces fonctionnalités sont une nécessité pour le style de programmation fonctionnel [...]." (c'est-à-dire: pas "la caractéristique déterminante") et répertorie plusieurs autres caractéristiques sur la page sur FP.
@ delnan, Très bien, j'ai corrigé ma formulation. Je voulais juste dire la condition pour qu'une langue soit considérée comme supportant un paradigme fonctionnel - tout comme la page Wikipedia pour chacune de ces langues que j'ai énumérées. Je ne voulais pas dire que c'est tout ce qui fait partie du style de programmation fonctionnelle .
Nicole
0

Je me suis demandé la même chose moi-même. Une fois que je suis dans une langue avec les lambdas, je les utilise beaucoup. Même PHP s'améliore quand vous réalisez que vous pouvez faire une fermeture avec php 5.3 (ce n'est pas aussi agréable que lisp, mais c'est encore mieux)

Zachary K
la source