Quel est le fondement mathématique des valeurs de première / deuxième / troisième classe dans les langages de programmation?

19

Ajoutée

Je viens de trouver deux questions connexes

/math//q/1759680/1281

/programming//a/2582804/156458


Dans les langages de programmation, de Michael Scott's Programming Language Pragmatics

En général, une valeur dans un langage de programmation est considérée comme ayant un statut de première classe si elle peut être transmise en tant que paramètre, renvoyée par un sous-programme ou affectée à une variable. Les types simples tels que les entiers et les caractères sont des valeurs de première classe dans la plupart des langages de programmation. En revanche, une valeur de «deuxième classe» peut être transmise en tant que paramètre, mais pas renvoyée d'un sous-programme ou affectée à une variable, et une valeur de «troisième classe» ne peut même pas être transmise en tant que paramètre.

Les étiquettes sont des valeurs de troisième classe dans la plupart des langages de programmation, mais des valeurs de deuxième classe dans Algol. Les sous-programmes affichent le plus de variations. Ce sont des valeurs de première classe dans tous les langages de programmation fonctionnels et la plupart des langages de script. Ce sont également des valeurs de première classe en C # et, avec quelques restrictions, dans plusieurs autres langages impératifs, dont Fortran, Modula-2 et -3, Ada 95, C et C ++. 11 Ce sont des valeurs de deuxième classe dans la plupart des autres langages impératifs, et des valeurs de troisième classe dans Ada 83.

  1. Quel est le fondement mathématique des valeurs de première / deuxième / troisième classe dans les langages de programmation?

    La terminologie me rappelle la logique du premier / deuxième ordre, mais sont-elles liées?

  2. Il me semble que la différence entre eux est de savoir dans quel cas spécifique une valeur peut être utilisée

    • passé en paramètre,
    • rentré d'un sous-programme, ou
    • assigné dans une variable.

    Pourquoi les cas spécifiques sont-ils importants, alors que les autres cas ne sont pas mentionnés?

Merci.

Tim
la source
23
La classification des valeurs en première / deuxième classe est aussi infructueuse que la classification des langues en paradigmes. Vous ne vous retrouverez qu'avec de vagues généralisations qui brouillent le tableau. Ce n'est pas la bonne approche pour comprendre les langages de programmation. Ce qui est important, c'est de comprendre la syntaxe, la statique et la dynamique du langage, mais c'est beaucoup trop grand pour entrer dans un commentaire
gardenhead
3
À part: PL / I serait un exemple d'étiquettes de première classe. Dans PL / I, vous pouvez déclarer des variables tapées pour étiqueter, leur affecter des emplacements de code et les aller. Vous pouvez également les passer en tant que paramètres et même en créer des tableaux.
Theraot
@Theraot: Peut-être que je sors avec moi-même, mais suis-je le seul à avoir eu à gérer des GOTO attribués et calculés dans FORTRAN? Et non, je n'ai pas écrit le @ $%! code, je suis resté coincé re-ingénierie.
jamesqf
@jamesqf Je suis conscient de l'attribution d'étiquettes dans FORTRAN et COBOL, non, je ne l'ai pas utilisé. Je ne sais pas comment classer les étiquettes là-bas. Pourtant pour ce que j'ai lu (j'ai les manuels) PL / I va au-delà, et je suis convaincu que les labels y sont de première classe.
Theraot

Réponses:

45

Il n'y en a pas et c'est assez arbitraire.

La seule distinction utile est entre la première classe et toutes les autres. Chaque cas qui se trouve dans la fourchette «autre» a son propre ensemble de règles distinct dans chaque cas et les regrouper tous ensemble n'est tout simplement pas très utile. "Première classe" signifie "Vous n'avez pas à chercher les règles", et "autre" est "Vous devez apprendre les règles".

Par exemple, en C ++, les fonctions individuelles sont des valeurs de première classe, tant qu'elles sont sans état. Les ensembles de surcharge ne le sont pas mais les lambdas le sont. En C #, les fonctions sont généralement des valeurs de première classe, mais il y a des cas gênants qui surviennent lors de l'inférence de type qui les empêchent d'être dans tous les cas.

DeadMG
la source
10
+1, bien que dans le contexte d'un livre comme Programming Language Pragmatics qui compare un grand nombre de constructions dans un grand nombre de langages et analyse en profondeur les similitudes, les différences et les implications, je pense que ce type de raccourci peut être utile. (Il suffit de ne pas s'attendre à ce que d'autres personnes comprennent que «d'occasion» et «troisième main» signifient des choses spécifiques.)
ruakh
(Euh, où par "seconde main" et "troisième main" j'entends bien sûr "deuxième classe" et "troisième classe". :-P)
ruakh
3
Si vous voulez faire du commerce avec des fonctions d'occasion, vous feriez mieux de le faire dans une langue où les fonctions sont des citoyens de première classe. ^^
5gon12eder
@ 5gon12eder: les "fonctions de seconde main" seraient celles que vous appelez à partir de bibliothèques tierces, non?
jamesqf
11

Je suis d'accord avec DeadMG, la distinction importante est entre la première classe et "tout le reste". Cependant, il existe un moyen plus familier de classer la différence.

Les valeurs de première classe sont des données, les autres sont du code. (en gros: je suis sûr qu'il y a des exceptions. Mais c'est une très bonne approximation qui vaut pour les langues du monde réel.)

Dans certaines langues, vous pouvez traiter le code comme des données. Les langages fonctionnels sont réputés pour cela: certains d'entre eux vous permettent de changer le code du programme en cours d'exécution (fondement de la programmation génétique ).

Les langages tels que C et C ++ vous permettent de prendre l'adresse des fonctions: bien que vous ne puissiez pas les modifier, vous pouvez passer des fonctions comme paramètres à d'autres fonctions. C ++ possède également le sucre syntaxique des foncteurs . L'idée est de créer un objet entier qui, en surface, semble se comporter comme une fonction et peut être transmis comme s'il s'agissait de données. Ce qui est autrement une classe de valeur inférieure peut être traité comme une valeur de première classe.

Mathématiquement, je pense que la meilleure façon est de penser à l' AST d'un programme . En règle générale, chaque jeton a un type spécifique qui peut être compatible ou non avec d'autres types. Pensez à la valeur l, à la valeur r et à tout le reste des types de valeurs en C ++ . Ajoutez ensuite les mots-clés, les symboles qui sont des fonctions, etc. Certains d'entre eux peuvent être des valeurs de première, deuxième ou troisième classe selon la langue.

Je ne suis pas sûr de savoir que la "classe" de la valeur est si importante, sauf peut-être dans un environnement académique. Dans le monde réel, la chose importante à savoir est de savoir comment transmettre du code, en le traitant comme des données: foncteurs, lambdas / fermetures, pointeurs de fonction, etc.


la source
2
Un exemple de valeur non codée non de première classe: Lua a une ..."variable" spéciale utilisée pour désigner les paramètres de la fonction vararg. Vous pouvez l'utiliser dans de nombreuses expressions comme s'il s'agissait d'une liste de valeurs (comme print(...)ou local args = {...}) mais il y a certaines choses que vous ne pouvez pas faire avec, comme y faire référence dans une fermeture ( function(...) return function() return ... end end).
Colonel Thirty Two
8

La sémantique dénotationnelle fournit une base mathématique pour décrire le fonctionnement des valeurs et des variables dans un langage de programmation. Cela a été si bien expliqué dans mon diplôme en informatique que j'ai obtenu une note élevée à l'examen de sémantique dénotationnelle, puis j'ai oublié la plupart et je n'ai jamais eu besoin de l'utiliser dans une vie de 20 ans en tant que programmeur.

Vous pouvez choisir d'utiliser une base mathématique bien définie, ou vous pouvez utiliser une terminologie informelle comme «statut de première classe». J'aurais appris beaucoup plus si le cours était basé sur la pragmatique du langage de programmation de Scott, mais les mathématiques formelles sont nécessaires pour quelqu'un qui va faire un doctorat en conception de langage de programmation.

Si vous lisez les spécifications de la plupart des langages de programmation, vous remarquerez un manque dissident de sémantique dénotationnelle, mais la plupart des langages bien conçus avaient quelqu'un dans l'équipe qui est un expert en conception de langage de programmation, donc comprend bien la sémantique dénotationnelle.

Michearl Scott utilise donc une terminologie informelle qui a une certaine relation avec les mathématiques formelles, tout en présentant le sujet d'une manière dont la plupart des programmeurs peuvent bénéficier. Sa terminologie n'est pas utilisée par d'autres personnes, donc elle n'est pas utile pour communiquer, mais elle vous donne une bonne base sur les questions que vous devriez poser lorsque vous voyez un nouveau langage de programmation pour la première fois.

Notez que Michael L. Scott est un chercheur de premier plan en informatique, donc il comprendra et sera très heureux d'utiliser les mathématiques formelles, mais comme les meilleurs chercheurs, il est habile à expliquer l'application de la recherche au reste d'entre nous.

Ian
la source
Merci. Quels livres ont été utilisés dans votre classe? Quels livres recommandez-vous maintenant? Je suis un auto-apprenant
Tim
@Tim, j'ai fait ma classe il y a plus de 20 ans! Je m'attends à ce que le livre de Michearl Scott soit l'un des meilleurs et couvre plus que ce dont vous avez besoin à moins que vous ne fassiez des recherches de niveau PHd.
Ian
3

Non, le mot «premier» dans «première classe» et dans «premier ordre» signifie des choses différentes.

Mais oui, les concepts de "première classe" et de "premier ordre" sont liés. Ils visent tous deux à classer les concepts sur lesquels une langue peut décrire la langue.

Un concept est de première classe si les mécanismes d'abstraction habituels d'un langage peuvent s'abstraire sur ce concept.

Par exemple, le langage de programmation Java peut décrire des entiers, et tous les mécanismes habituels pour abstraire sur des entiers (les accepter comme paramètres de méthode, les renvoyer comme résultats de fonction, les stocker dans des structures de données, ...) fonctionnent pour des entiers.

Un concept est de premier ordre s'il ne peut pas être utilisé pour s'abstraire sur lui-même.

Par exemple, encore une fois en Java, nous pouvons utiliser des méthodes pour résumer certaines choses. Mais nous ne pouvons pas utiliser de méthodes pour abstraire des méthodes, car un nom de méthode ne peut pas être passé comme paramètre de méthode. Ceci est différent en JavaScript, où vous pouvez utiliser la notation entre crochets pour accéder à une propriété d'un objet par son nom sous forme de chaîne, et vous pouvez résumer sur des chaînes.

Un concept est de second ordre s'il peut être utilisé pour faire abstraction des utilisations de premier ordre de lui-même, mais pas des utilisations de second ordre.

Par exemple, en Java, vous pouvez utiliser des génériques pour abstraire des types (comme dans class Foo<T> { public List<T> content; }). Cependant, vous ne pouvez pas utiliser de génériques pour abstraire des génériques (comme dans class Bar<T> { public T<Int> content; }). C'est différent à Scala.

Un concept est de troisième ordre s'il peut être utilisé pour faire abstraction des utilisations de premier ordre et de deuxième ordre de lui-même, mais pas des utilisations de second ordre.

Etc.

Enfin, un concept est d'ordre supérieur s'il peut être utilisé pour abstraire des utilisations arbitraires de lui-même.

Résumé: Si une fonction d'abstraction est de première classe, elle est également d'ordre supérieur. Et si une fonction d'abstraction est de premier ordre, elle ne peut pas être de première classe.

Toxaris
la source
1
Vous pouvez le faire List<List>en Java, cependant. Peut-être pouvez-vous clarifier ce que vous voulez dire avec cette partie?
Polygnome
1
Bon point. Je veux dire: class Foo<A> { A<Int> ... }. C'est-à-dire que je veux utiliser un paramètre de type générique comme classe générique. Ensuite, Foo<List>instancierait le A<Int>to List<Int>. En Java, ce n'est pas possible. Je vais essayer de modifier cela dans la réponse plus tard.
Toxaris
En effet, ce n'est pas possible.
Polygnome
J'ai maintenant remplacé l' List<List>exemple trompeur . Merci d'avoir signalé le problème, @Polygnome.
Toxaris
@Toxaris Je pense que c'est juste une terminologie idiosyncrasique que vous avez inventée vous-même. Un «concept» n'est pas de premier ordre ou de second ordre, un quantificateur logique l'est.
Miles Rout
2

Quel est le fondement mathématique des valeurs de première / deuxième / troisième classe dans les langages de programmation?

Aucun que je sache.

La terminologie me rappelle la logique du premier / deuxième ordre, mais sont-elles liées?

Pas vraiment.

La "classe" d'un élément de langage de programmation n'est qu'une manière abrégée de réfléchir à la question de savoir ce que l'utilisateur de mon langage souhaite manipuler par programme . Par exemple, C # vous offre un ensemble riche d'opérations pour manipuler des valeurs , un ensemble moins riche de façons de manipuler des types et aucune opération qui manipule des étiquettes .

Cependant, votre intuition selon laquelle il existe un lien ici n'est pas entièrement déplacée. Il y a une analogie à faire entre la logique du premier ordre et la programmation procédurale, et de la logique d'ordre supérieur à la programmation fonctionnelle. La logique du premier ordre concerne la manipulation logique des valeurs; la programmation procédurale concerne la manipulation programmatique des valeurs. La logique d'ordre supérieur concerne la manipulation logique des énoncés de logique , la programmation fonctionnelle concerne la manipulation programmatique des fonctions .

Pourquoi les cas spécifiques sont-ils importants, alors que les autres cas ne sont pas mentionnés?

Il faudrait demander à l'auteur une réponse définitive.

Je ne serais pas trop accroché à cette notion de "classe". Ce n'est pas une chose formellement définie. C'est un raccourci que les concepteurs de langage utilisent pour parler de ce qui peut être manipulé par programme.

Eric Lippert
la source
2

La «valeur de première classe», dans ce contexte, est une terminologie standard dans la théorie du langage de programmation. Une valeur de première classe est quelque chose qui peut être manipulé comme des valeurs normales dans le langage, quelque chose qui peut être calculé au moment de l'exécution. Bien sûr, c'est une définition tautologique jusqu'à ce que vous ayez défini une sémantique pour le langage, puis une valeur est ce que la sémantique définit comme étant une valeur. Le but du concept est d'identifier ce qui peut être manipulé directement, par opposition à seulement accessible indirectement.

Par exemple, dans presque tous les langages de programmation, les entiers machine d'une taille limitée (par exemple les entiers 8 bits, ou les entiers 32 bits, ou les entiers 64 bits, etc.) sont des valeurs de première classe. Vous pouvez les stocker dans des variables, les transmettre et les retourner aux fonctions, etc. Dans la plupart des langages mais pas dans les langages de bas niveau tels que l'assemblage et le C, les chaînes sont des valeurs de première classe - mais en C, elles ne le sont pas, vous seulement obtenir des pointeurs sur les chaînes. En C, les chaînes et les tableaux ne sont pas des valeurs de première classe: par exemple, vous ne pouvez pas passer un tableau à une fonction, vous ne pouvez pas affecter un tableau à une variable de tableau, etc. En C, les fonctions ne sont pas des valeurs de première classe soit: vous ne pouvez pas stocker une fonction dans une variable, seulement un pointeur sur une fonction. En revanche, les chaînes et les fonctions sont des valeurs de première classe dans la plupart des langages de programmation de haut niveau: vous pouvez les stocker dans une chaîne, etc.

Les types sont un exemple d'un concept qui n'est pas de première classe dans de nombreux langages de programmation conçus pour être compilés. Dans un langage comme C ou Java, les types vivent au moment de la compilation, vous ne pouvez pas les manipuler à l'aide de constructions de langage. (Java a également un système de type dynamique basé sur des classes; les classes sont des valeurs de première classe par réflexion.) En revanche, un langage comme Python a une typefonction qui renvoie une valeur qui représente le type de son argument.

La négation de la «valeur de première classe» dans la terminologie standard n'est «pas une valeur de première classe». Le terme «valeur de deuxième classe» n'est pas couramment utilisé et «valeur de troisième classe» encore moins. Ne vous attendez pas à les voir en dehors de ce livre. Il n'y a absolument aucune base pour définir «deuxième» comme «peut être passé en tant que paramètre» et «troisième» comme «ne peut pas être passé en tant que paramètre», il n'y a pas d'échelle de choses qui pourraient être sérieusement numérotées. Très peu de langues font la différence entre les valeurs qui peuvent être transmises en tant que paramètre aux fonctions et les valeurs qui peuvent être affectées aux variables, il n'est donc pas utile de définir un nom pour ce concept.

Gilles 'SO- arrête d'être méchant'
la source