Les attributs des langages de programmation et leurs progrès

Des progrès ont été réalisés pour enrichir les langages et améliorer la productivité. Voici la liste de toutes les fonctionnalités dont peut être doté un langage.

La première implémentation dans un langage est indiquée entre parenthèses.

Variables

Inférence de type

Le type d'une variable est déduit de la valeur qui lui est assignée. Cela dispense le programmeur d'indiquer explicitement un type, une maigre économie en fait. (ML 1973).

Interpolation de chaîne

On intègre des variables dans une chaîne de caractères, qui sont remplacées par leur valeur lors de l'exécution. Par exemple en PHP:

 $a = 5; $str="$a items";

Ce qui affichera: 5 items.

Généricité / polymorphisme paramétrique

La possibilité de remplacer un type défini comme entier ou chaîne par un type indéfini comme paramètre d'une fonction, afin d'appliquer le même traitement à des données de types différents sans besoin de réécrire le code.

Sous-typage (subtype)

Hiérarchie dans les types d'un langage qui fait qu'une opération sur un type peut aussi s'exécuter sur un sous-type alors qu'elle ne peut l'être sur des types différents. Par exemple entier serait un sous-type de nombre et a les mêmes opérations, contrairement au type chaîne. (Simula 67).

Assignement avec valeur par défaut

On assigne le contenu d'une variable si elle n'est pas null, sinon on assigne la valeur de remplacement indiquée par l'opérateur ?? (C#)

var x = y ?? 0   

Hoisting

Le hoisting: consiste a déplacer au début du même espace de visibilité, le scope, tout ce qui est déclaré à l'intérieur. Les variables déclarées dans le corps d'une fonction le seront par l'interpréteur au début de la fonction. On peut appeler une fonction définie après parce qu'elles sont toutes déclarées par l'interpréteur au début du script (mais la definition est analysée dans son emplacement dans le code). JavaScript utilise le hoisting.

Fonctions

Anonyme ou lambda

Une fonction peut être définie de façon anonyme si on l'assigne à une variable ou si on la passe comme argument d'une autre fonction. (Lisp 1958).

Clôture

Consiste à déclarer une fonction à l'intérieur d'une autre, et de l'assigner comme valeur de retour. Etant incluse dans l'autre fonction, elle a accès à ses variables, cela permet donc de lire et modifier des variables déclarées à l'intérieur d'une fonction à partir d'un scope plus global.
Pour utiliser une clôture, on assigne la valeur de retour de la fonction container à une variable, et on traite cette variable comme une fonction. JavaScript utilise la clôture comme constructeur d'objet, doté de paramètres. (Scheme 1975).

Contrat (programmation par)

Assurer que les méthodes et objets remplissent certaines conditions. Dans le cas d'une fonction, on instaure des préconditions sur les arguments et des post-conditions sur la valeur de retour. Elles sont obligatoires dans un langage qui instaure cette conception, mais on peut les ajouter dans tout langage. (Eiffel, 1986).
Exemple:

put(message : STRING) is
require
  not message.empty     -- la chaîne ne doit pas être vide
do
  ... instructions...
ensure 
  ... -- conditions sur le message pour accepter le retour

end

Monade

Fonction qui peut être enchaînée parmi d'autres, ses arguments étant les résultats d'autres fonctions, et sa valeur de retour servant d'argument à d'autres fonctions.

Paramètre nommé

Appel d'une fonction avec le nom d'un argument et la valeur qu'on lui assigne. Exemple pour une fonction dont les arguments sont int x et int y:

point(x : 10, y: 20)

Cela permet de donner les paramètres dans un ordre quelconque et d'en omettre s'ils sont optionnels. (IDL 1977).

Première-classe

Qualité générale, mais rare pour les fonctions: la possibilité de les utiliser comme des variables. Ainsi une fonciton peut être passée comme argument, ou comme valeur de retour... voir clôture.

Staged (délayée)

La programmation délayée consiste à definir des fonctions qui s'exécutent lorsque les données requises sont disponibles et selon le type de données. Elle peuvent s'exécuter partiellement en étapes. Cela a été décrit dans un papier de William L. Scherlis en 1986 et implémenté dans Julia et autres langages.
Un compilateur réalise une sorte de programmation en stage puisque l'on définit comment des actions s'exécutent qui le sont ensuite en fonction des données (codes sources) que l'on donne au programme.

Tuple

Une fonction peut retourne plusieurs valeurs à la fois, qui forment un tuple. On peut ainsi assigner à un tuple comprenant plusieurs variable le résultat d'une fonction.

Itérations

Générateur

Le mot-clé yield dans une fonction renvoie une valeur tout comme l'instruction return, mais dans une boucle qui peut ainsi retourner plusieurs valeurs successivement. La valeur suivante est lue a chaque appel de la fonction qui engendre le calcul de la valeur suivante. Le traitement de la fonction est mis en pause quand le mot yield est rencontré, la valeur est retournée, et il reprend avec l'appel suivant. (CLU 1975).

Itérateur

Object doté de méthodes pour scanner le contenu d'un tableau ou collection donnés en paramètre. (CLU 1975).

Liste en compréhension

Itérer le contenu d'une liste par une seule instruction qui le filtre et produit une autre liste. Cet exemple en Julia multiplie les éléments d'un tableau par deux. On peut remplacer x * 2 par une condition pour faire une sélection.

y = [ x * 2 for x in 1:50]

(SETL 1967).

Objets

Aspect

Un aspect est un ensemble de concerns, et un concern est un groupe d'instruction qui interagit avec les objets d'un programme. On peut remplacer globalement un aspect par un autre pour appliquer un programme à un domaine ou un type de traitement différent. (Extension à Java Hyper/J en 2001 par IBM).

Héritage

L'héritage ajoute à une classe les attributs et méthodes de la classe dont elle hérite. (Simula 67).

Interface

Sorte de classe contenant des méthodes déclarées mais non définie. Quand une classe hérite d'une interface, elle doit donner une définition à chacune de ses méthodes.

Message ou évènement

Les objets peuvent interagir en appelant leurs méthodes respectives, comme dans C++, ou il peuvent le faire comme dans Smalltak ou Objective-C en s'envoyant des messages. Chaque objet dispose d'écouteurs dans l'attente de messages.
Les langages orienté-évènement comme JavaScript peuvent aussi supporter ce mode d'interaction. (Smalltalk 1970).

Mixin/trait

Classe ayant accès aux méthodes d'autres classes. Pratiquement à laquelle on ajoute des interfaces dont les méthodes ont une définition et ayant aussi des attribut. L'ajout des méthodes peut se faire au runtime. (Flavor, 1986)
Un trait est un mixin sans attribut.

Modèle prototype (Prototype pattern)

Inventé dans Smalltalk (1970), et repris à sa façon par JavaScript, consiste à créer des objets dynamiquement et remplacer l'héritage par le clonage. L'avantage sur le modèle des classes est de permettre au programme de créer et modifier lui-même les objets.

Syntaxe

Surcharge des opérateurs / polymorphisme ad hoc

Les opérateurs sont redéfinis dans le programme pour effectuer des opérations différentes selon les objets qu'ils relient. (ALGOL 68).

Expression régulière

Séquence de caractère représentant une commande de recherche et éventuellement remplacement dans un texte, en accord avec une grammaire. (Inventeur Stephen Kleene en 1956, langage AWK en 1977).

Extensibilité

Certains langages comme TCL permettent d'ajouter de nouveaux constructs au langage lui-même.

Macro paramétrée

Construct doté de paramètres auquel on associe une définition qui remplacera la construct lors de la compilation.

Opérateur ternaire

La formulation: if x == y then a1 else a2 peut être remplacée par:

x == y ? a1 : a2 

Slice

Syntaxe simplifié pour lire ou remplacer une rangée d'élément dans un tableau. En ALGOL 68 déjà, on écrit [ x : y ] pour désigner l'intervalle compris entre x et y dans la liste des éléments.

Processus

But

On définit un but qui est un assignement ou un appel de fonction, qui se fait quand un processus retourne un résultat souhaité. Donc au lieu d'écrire si cond alors action on écrit action si condition.
Cette structure prend tout son sens si la condition est le résultat d'un traitement réalisé répétitivement jusqu'à pouvoir atteindre le but. (Planner, 1969).

Concurrence

Capacité de d'exécuter des traitements en parallèles, eventuellement avec une interaction entre eux. (Décrit par Dijkstra en 1965).

Contrainte

Au lieu de définir les étapes d'un traitement, chacune dépendant de l'accomplissement de la précédente, on définit les propriétés d'un élément, lorsque toutes les propriétés ont une valeur voulue, une action s'exécute. (Prolog III 1989).

Coroutine

Bloc d'instruction (routine, fonction) dont le traitement s'interrompt ou reprend en fonction d'ordres extérieurs. Tandis qu'une fonction s'interompt sur une instruction de retour. (Simula 67).

Distribué

Capacité pour un programme de faire tourner des modules ou objets sur des ordinateurs différents. Cela va au-delà de la concurrence qui opère sur un même ordinateur.

Exception

La gestion d'exceptions est la capacité d'un langage à intercepter les erreurs pouvant interrompre un programme et rediriger le flux sur un bloc d'instruction qui permet sa continuation. (Lisp 1958).

Logique

En programmation logique, les actions sont des évaluations et le flux est dirigé par la résolution d'expressions. C'est le cas de Prolog, et cela a été ajouté comme extension à d'autres langages.
Le premier langage d'inférences logique est Prolog (1972).

Pattern matching

Confronte une expression à une série de conditions possible et exécute l'action associée à la condition qui lui correspond. (SNOBOL 1962).

Promise

Appelé aussi eventual, future, delay, await, un promise est un objet utilisable lorsque ses champs seront effectivement remplis et que ses attributs auront une valeur. Lorsque cela devient le cas, il devient actif et pris en compte par le traitement en cours. Implémenté par de nombreux langages dont C++ 11, JavaScript, Scala, Visual Basic 11, C# 5.
En JavaScript + HTML part exemple, un promise peut être créé avec l'action de l'utilisateur, et une réponse fournie de façon asynchrone lorsque les valeurs dont il a besoin sont obtenues du serveur.
Concept originellement présenté par plusieurs auteurs entre 1976 et 1977, cela aurait été d'abord implémenté dans Act 1 (1981) et MultiLisp (1985).

Réactive (programmation)

Les variables ne sont pas assignées statiquement mais dynamiquement: leur contenu dépend du contenu d'autres variables et sont mis à jour que le contenu de ces autres variables change. C'est le principe de fonctionnement des tableurs ou le contenu d'une cellule dépend de celui d'autres cellules. Remplaçons le tableur par une table de base de données et l'on peut décrire un traitement en quelques lignes de code.

Réflexion

Capacité donnée au programme de pouvoir analyser son propre code et l'améliorer en fonction des résultats. Cela suppose une construction du programme sous forme d'arborescence sous-jacente.

Mémoire

Garbage collector

Gestionnaire de l'allocation mémoire qui périodiquement, récupère les zones libérées pour supprimer la fragmentation et donner accès à un espace plus large en cours d'exécution. (Lisp 1960+).

Persistence

L'état des objets et éventuellement des variables est automatiquement sauvé lors de l'interruption d'un programme, et sont restaurés lorsqu'on le relance. (MATHLAB 1964).

RAII (Resource Acquisition Is Initialization)

Gestion automatique de mémoire introduite avec C++ où les emplacements sont libérés automatiquement quand les objets qui les utilisent sont supprimés. (C++ 1984).

Performances

Evaluation paresseuse

L'évaluation d'un argument ou d'une expression en général est reportée jusqu'au moment où le résultat est nécessaire pour la suite du traitement. Cela peut accélérer considérablement le traitement. (Décrit en 1976).

Multiple-dispatch

Compilation d'une fonction dont les arguments sont génériques, en plusieurs versions, une pour chaque type d'argument.

Portabilité

La portabilité passe par un langage intermédiaire (le premier étant UNCOL en 1958) fonctionnant sur une machine virtuelle implémentée sur chaque système.

AOT (Ahead-Of-Time)

Machine virtuelle qui compile le code d'un programme en code natif lors de la première exécution et réutilise le code natif lors des sessions ultérieures.

JIT (Just-In-Time)

Machine virtuelle qui compile le code source ou une partie du code source en langage natif lors de chaque session avant d'exécuter le code natif. (Lisp, 1958).


On s'aperçoit en fait, que la plupart des attributs de langages de programmation actuels existent depuis des décennies.

Voir aussi...

Paradigmes de la programmation.

Le futur des langages de programmation.