En Abstrasy, les symboles sont les libellés (ou les identifiants) utilisés pour identifier de façon unique des valeurs, des objets, des fonctions, ou tout autre élément ne faisant pas directement partie du langage de programmation 1). Les symboles servent donc à mettre en oeuvre l'abstraction nécessaire aux tâches que doit accomplir le logiciel. Les symboles sont donc les noms que le programmeur va pouvoir établir pour étiqueter les éléments qui constituent les programmes ou qui sont traités par ceux-ci.
Les symboles peuvent être librement créés et utilisés dans les programmes. Cependant, ils doivent respecter un ensemble de règles de nommage particulières.
Ces règles de nommage sont très importantes car elles permettent non seulement de former des noms de symbole corrects , mais aussi elles documentent les données qu'elles symbolisent. Elles permettent donc non seulement de nommer les éléments que l'on souhaite utiliser dans les programmes, mais aussi de les manipuler d'une manière plus sûre.Cela n’exclut cependant pas une grande liberté de choix de noms symboliques. Voici l'ensemble des règles de nommage des symboles en Abstrasy:
Les symboles suivants sont correctement formés:
Par contre, les exemples de symboles suivants sont incorrects:
En plus des règles de nommage qu'il faut absolument respecter, il est conseillé de conformer autant que possible les nouveaux symboles aux conventions de nommage facultatives du langage. Cela fait partie des bonnes pratiques du langage de programmation.
Si vous ne respectez pas ces convention facultatives, cela n'empêchera pas vos programmes de fonctionner correctement. Toutefois, ils pourront être difficiles à relire, à maintenir, ou encore à être réutilisés. Il est donc fortement conseiller de conformer vos symboles aux conventions de nommage facultatives du langage de programmation.
Il est plus facile de reconnaître une fonction ou une méthode de prédicat si son nom se termine par un point d'interrogation ?.
Cette convention est en adéquation avec celle des mots réservés du langage où les nom des opérateurs prédicats se terminent aussi toujours par un point d'interrogation (par exemple: boolean?, ==?, <?, >?, integer?, etc…).
Il est également plus sûr signaler une fonction ou une méthode qui produit un effet de bord. Il peut s'agir par exemple d'une fonction qui affecte la valeur d'un de ses paramètres. Pour cela, il convient de signaler cette particularité en terminant le nom symbolique d'une telle fonction par un point d'exclamation !.
Cette convention est également conforme à celle utilisée dans les mots réservés du langage où les opérateurs à effet de bord sont signalés par un nom qui se termine par un point d'exclamation (par exemple, les opérateurs atomiques comme set!, incr! ou encore fetch-and-add!).
Notez toutefois que l'opérateur swap!! a la particularité de signaler à la fois un effet de bord et aussi un usage déconseillé dans un contexte parallélisé. C'est pourquoi, il se termine par deux points d'exclamation, chose qui n'est pas permise dans les symboles.
Lorsqu'un nom de symbole commence par un underscore '_', cela indique que le symbole désigne une fontion ou une méthode d'implémentation.
Généralement, les fonctions d'implémentation sont des fonctions appelées par d'autres fonctions qui s'en servent pour implémenter tout ou partie d'une procédure. Il est donc tout à fait possible qu'une méthode d'implémentation ne fournisse qu'une partie du programme effectivement réalisé par une fonction.
On parle donc assez souvent d'implémentation partielle. C'est notamment le cas des méthodes _impl_incr_mutator, _impl_decr_mutator ou encore _impl_add_mutator qui implémentent partiellement non seulement les opérateurs incr!, decr! et add!, mais aussi fetch-and-incr!, fetch-and-decr! ainsi que fetch-and-add!.
Comme nous l'avons dit plus haut, Abstrasy impose le typage statique du contenant. Cela signifie que le symbole d'une variable (son identifiant) fournit une documentation sur le contenant, c-à -d des informations sur la manière dont peut être utilisé la variable. On dit aussi que le typage est statique parce qu'il fait partie du symbole. L'information est donc statiquement liée au symbole lui-même.
La principale information fournie est la capacité ou non de pouvoir affecter le contenu de la variable. Ainsi, si le symbole d'une variable commence directement par une lettre minuscule [a-z] ou majuscule [A-Z], cela signifie que la variable est immuable. On ne peut donc pas affecter son contenu. Pour indiquer qu'une variable est mutable, son symbole doit commencer par le caractère dollar $. Dans ce cas, le symbole est lié à une cellule d'indirection qui fournit une référence vers une donnée. Dans ce cas, bien qu'on ne puisse pas modifier le lien qu'il y a entre le symbole et la cellule d'indirection, la référence contenue dans cette cellule peut être affecter à une autre donnée (du même type dynamique de contenu uniquement - Pour en savoir d'avantages référez-vous à la section «Les variables»).
D'un point de vue absolu, une variable mutable implique un effet de bord. Or, il est très difficile de manipuler des données si on ne peut absolument rien faire d'autre avec que les lire.
Dans certains langages de programmation fonctionnelle dite «pure», on ne peut affecter aucune variable. Si on veut donner l'impression de changer le contenu d'une variable, il faut créer un nouveau contexte et déclarer une nouvelle variable immuable avec le même nom symbolique. La nouvelle variable masque donc celle du contexte précédent mais ne la remplace pas. On peut aussi faire cela en Abstrasy. Toutefois, cette stratégie implique des contraintes supplémentaires au niveau du style de programmation.
Abstrasy ne fait donc pas partie des langages de programmation fonctionnelle «pure» tout simplement parce qu'il autorise les variables mutables. Cependant, comme un symbole ne peut être lié qu'à un seul objet et que cette liaison est immuable, le principe de l'assignation unique est respecté. Abstrasy est donc bien un langage de programmation fonctionnelle. Toutefois, il se rapproche d'autres langages comme OCaml où on peut utiliser des objets «références» pour obtenir un résultat similaire.
Le symbole d'une variable immuable commence toujours par une lettre [A-Z] ou [a-z].
Lorsqu'on a assigner une valeur à variable immuable, la valeur de cette variable ne peut plus être modifiée dans le contexte courant. La variable peut toutefois être redéfinie dans un autre contexte, même éventuellement imbriqué.
(define 'x 10) (if{=? x 10} { (define 'x 20) (display "1. x = " x) } ) (display "2. x = " x)
⇒
1. x = 20 2. x = 10
Une variable immuable contient la référence directe de la valeur (qui est une donnée dans la mémoire vive de l'ordinateur).
Le symbole d'une variable mutable commence toujours par un dollar $. Cela signifie qu'il est possible de muter la valeur assignée à la variable.
(define '$x 0) (set! $x (+ $x 1)) (display "$x = " $x)
⇒
$x = 1
Comme dans le cas des variable immuables, une variable mutable reçoit une référence qui ne change plus après assignation. Toutefois, une variable mutable ne fournit pas la référence directe d'une donnée. On lui assigne plutôt une cellule d'indirection ref dont le rôle consiste à stocker la référence de la donnée de destination. Ainsi, la référence assignée à la variable est immuable, par contre, la référence contenue par la cellule d'indirection référencée peut, quant à elle, être modifiée. Il s'agit donc d'une référence indirecte, mais dont l'usage est totalement transparent pour l'utilisateur.
Un nom symbolique peut aussi être composés de plusieurs symboles. Ceci est particulièrement utile dans le cadre de la programmation par objet où une donnée peut être une propriété d'une objet. Les symboles peuvent donc être joints à l'aide de l'opérateur de résolution de portée qui est représenté par le caractère deux points «:». Ainsi, par exemple, on peut accéder à la propriété hauteur de l'objet rectangle en utilisant le symbole rectangle:hauteur. Ce principe très simple est aussi utilisé d'une manière similaire dans d'autres langages de programmation par objet.
On notera cependant qu'un symbole est toujours considéré d'une manière unitaire. L'opérateur de résolution de portée fait partie du symbole. Ainsi, dans rectangle:hauteur, il n'y a qu'un seul symbole et non deux.
Nous avons vu dans la rubrique précédente qu'il est possible de composer des symboles pour pouvoir accéder aux propriétés et aux méthodes des objets à l'aide de chemins d'accès absolus. Cependant, il est aussi possible de résoudre des symboles qui fournissent des chemins d'accès relatifs. Pour cela, il est nécessaire de parler à présent des symboles spéciaux.
Pour résoudre les symboles et accéder aux données, Abstrasy utilise une méthode analogue à celle de la résolution des chemins d'accès des fichiers sur un disque. On pourrait voir, tout simplement, la résolution du symbole rectangle:hauteur comme étant le chemin d'accès du fichier «hauteur» dans le dossier «rectangle».
Comme dans un système de fichiers, il est possible de résoudre un symbole relatif à l'objet courant comme s'il s'agissait d'accéder à un fichier à partir du dossier courant. Ainsi, à partir d'une méthode appartenant à un objet, on peut accéder aux propriétés de l'objet d'une manière relative à l'aide du symbole spécial point «.». Ainsi, par exemple, le symbole .:hauteur permet d'accéder à la propriété hauteur de l'objet courant. Le symbole spécial . fournit le contexte de l'objet courant, c-à -d l'équivalent de l'opérateur (self). L'utilisation du symbole spécial . permet toutefois de pouvoir composer des symboles relatifs plus aisément.
Pour une raison de concision, le symbole . peut être omis s'il constitue la premier partie d'un symbole composé. De cette manière, .:hauteur est sémantiquement identique à :hauteur. On peut donc utiliser la forme concise sans que cela n'altère la signification du symbole.
Un des avantages de la programmation par objet consiste à permettre la redéfinition des propriétés et des méthodes d'un objet parent dans un objet dérivé. L'objet dérivé possède donc toutes les caractéristiques de son parent, mis à part ses propres spécifications. On appelle cela l'héritage.
Bien sûr, lorsqu'on redéfinit une méthode, la nouvelle méthode de l'objet dérivé masque simplement la méthode de l'objet parent. La méthode de l'objet parent reste donc toujours accessible. Ainsi, pour accéder au contexte de l'objet parent, il suffit d'utiliser le symbole spécial deux points successifs «..». Donc, si à partir du contexte courant d'un objet, on résout le symbole ..:hauteur, on accède à la propriété hauteur de l'objet parent dont l'objet courant est dérivé. Par analogie, on peut dire qu'il est possible de remonter l'enchaînement de l'hérédité d'un objet comme si remontait dans l’arborescence des dossiers d'un système de fichiers en partant du dossier courant.
|
![]() |
|
||||||||