Mon premier programme en Archon
Le plus petit programme complet, ligne par ligne, à exécuter et à modifier.
Voici un programme Archon complet. Exécutez-le tel quel, puis changez le texte et exécutez-le de nouveau.
@entry(demarrer)
object MonProgramme {
demarrer() {
Console.print("Salutations archonites !");
}
}
Ligne par ligne :
@entry(demarrer)— la directive qui désigne le point de départ. Un programme Archon démarre à la méthode que@entrynomme, sur le type qui la contient. Ici, c’est la méthodedemarrerqui démarre. Historiquement, beaucoup de langages ont fait du nom de la méthode la convention : en C, et dans tout ce qui l’a suivi, le programme démarre àmainparce qu’elle s’appellemain. Archon le dit par une directive plutôt que par un nom réservé, et la conséquence est directe : le point d’entrée porte le nom que vous voulez.demarrerici,mainsi vous y tenez,lanceroucommencer— c’est@entryqui tranche.object MonProgramme { … }— le type qui porte le programme.objectdéclare un objet : un type dont il n’existe jamais qu’une seule instance, et cette instance existe déjà quand le programme démarre — rien ne l’instancie, il n’y a pas de constructeur à écrire. C’est exactement ce qu’est un programme : il n’y en a qu’un. En Archon, aucune instruction ne vit au niveau du fichier : il n’existe pas de fonction libre, donc pas de code libre non plus. Tout ce qui s’exécute appartient à une méthode, et toute méthode appartient à un type.demarrer() { … }— la méthode de départ. Aucunstaticici : l’unique instance deMonProgrammeest déjà là, doncdemarrerest une méthode d’instance ordinaire. Sur unarchetype— le type à plusieurs instances, le concept central du langage, que la section 7 présente bientôt —@entrydevrait viser une méthodestatic, faute d’instance à ce moment-là.demarrerne prend aucun paramètre et ne retourne rien.Console.print("Salutations archonites !");— l’instruction qui affiche. L’argument est ici passé positionnellement, par son rang ; Archon permet aussi de le nommer, ce que montre la sous-section suivante. L’instruction se termine par un point-virgule.
Le compilateur vérifie ce que la directive promet : le nom désigné existe bien dans
MonProgramme (sinon P038), et la méthode ne prend aucun paramètre (sinon P041).
Le @, c’est une directive. @entry est la première d’une famille : en Archon, tout ce
qui commence par @ est une directive de précompilation — une consigne adressée au
compilateur, lue avant qu’il ne produise le programme, et jamais du code qui s’exécute.
Contrairement aux attributs de .NET ou aux annotations de Java, ce n’est pas un mécanisme
ouvert où chacun invente les siennes : l’ensemble est fermé, reconnu nativement par le
compilateur, chacune ayant un effet précis et documenté. Elles servent deux choses : agir
sur la compilation — @entry désigne le point de départ, @mode change le modèle de
mémoire, @extern lie une fonction d’une bibliothèque écrite ailleurs — et documenter,
avec @doc, qu’on verra plus bas.
Un object, c’est le singleton sans le cérémonial. Le besoin est vieux comme les langages
à objets : un type dont il ne doit jamais exister qu’un seul exemplaire. En Java, en C#, en
C++, le langage ne l’offre pas — il se construit, au prix d’un rituel que tout le monde a
écrit dix fois : rendre le constructeur privé pour que personne d’autre ne puisse instancier,
garder l’unique exemplaire dans un champ statique, l’exposer par une méthode getInstance()
qui le crée à la première demande, et se demander pour finir ce qui arrive si deux fils
d’exécution l’appellent en même temps. Une demi-page de plomberie pour dire « il n’y en a
qu’un » — et une garantie qui ne tient qu’aussi longtemps que personne ne contourne la
convention. Scala et Kotlin ont montré la sortie en en faisant un mot-clé ; Archon prend la
même : object, et c’est tout. Ce n’est plus une discipline à tenir, c’est une propriété du
type — rien ne peut en créer un second.
Seuls les mots-clés sont fixés par le langage : demarrer et MonProgramme sont des noms que
vous choisissez. Le cours anglais fait tourner le même programme avec des noms anglais —
start, MyProgram — et il se comporte à l’identique.
À vous
- Changez le texte entre guillemets.
- Ajoutez une seconde ligne
Console.print(…);sous la première. - Renommez
demarrerenmaindans la méthode — mais pas dans la directive. Exécutez, et lisez ce que le compilateur vous répond.
Nommer les arguments
Le même programme, à ceci près que l'argument est passé par son nom.
Voici le programme précédent, à un détail près : l’argument de Console.print est passé par
son nom.
@entry(demarrer)
object MonProgramme {
demarrer() {
Console.print(text: "Salutations archonites !");
}
}
Exécutez-le. La sortie est exactement celle de la version précédente — les deux écritures désignent le même appel.
Un paramètre a un nom, et ce nom peut servir à l’appel
print est déclarée static print(text: str) : un seul paramètre, nommé text, de type
str. Ce nom ne servait jusqu’ici qu’à l’intérieur de la méthode ; Archon permet de s’en
servir aussi à l’appel. D’où deux façons de passer une valeur :
- positionnelle —
Console.print("Salutations archonites !"): la valeur se place par son rang. La première valeur va au premier paramètre, la deuxième au deuxième. - nommée —
Console.print(text: "Salutations archonites !"): la valeur se place par le nom du paramètre qu’elle vise, suivi de deux-points.
Ce qui précède les deux-points est toujours le nom du paramètre tel que la méthode l’a déclaré — jamais celui de la variable qu’on lui passe.
L’ordre n’a plus d’importance
Avec un seul paramètre, la différence ne se voit pas. Elle apparaît dès qu’une méthode en
prend plusieurs — et comme Console.print n’en a qu’un, écrivons-en une. saluer prend deux
paramètres, prenom et nom, et demarrer l’appelle trois fois :
@entry(demarrer)
object MonProgramme {
demarrer() {
self.saluer(prenom: "Simon", nom: "Magus");
self.saluer(nom: "Magus", prenom: "Simon");
self.saluer("Simon", "Magus");
}
saluer(prenom: str, nom: str) {
Console.print(text: "Bonjour " + prenom + " " + nom + " !");
}
}
Exécutez-le : trois lignes identiques. Les trois appels désignent la même méthode avec les
mêmes valeurs — nommé dans l’ordre de la déclaration, nommé dans l’ordre inverse, positionnel.
De saluer, ne retenez pour l’instant que sa première ligne : deux paramètres, chacun avec son
nom et son type, exactement comme print et son text. Le reste viendra en son temps — notez
seulement que self désigne l’objet lui-même : self.saluer(…), c’est MonProgramme qui
appelle sa propre méthode.
Les deux formes se mélangent même dans un seul appel, à condition que les positionnels viennent en premier :
self.saluer("Simon", nom: "Magus"); // valide
self.saluer(nom: "Magus", "Simon"); // refusé : erreur T027
Le refus n’est pas une commodité d’implémentation. Un argument positionnel se place à son
rang, et un argument nommé qui le précède rend ce rang illisible : dans le second appel,
"Simon" est au deuxième rang, mais la seule case encore libre est la première. Faut-il le lire
par son rang, ou comme « la prochaine case libre » ? Plutôt que de choisir une des deux
lectures en silence, le compilateur refuse — et il refuse même quand les deux lectures
tomberaient d’accord, pour que la règle se lise sans réfléchir.
Pourquoi nommer
Un appel nommé dit ce que chaque valeur est, sans qu’il faille aller relire la déclaration de la méthode. Surtout, il survit à l’ajout d’un paramètre : le nom vise toujours le bon, là où un appel positionnel change de sens sans rien dire. C’est la forme recommandée dès qu’une méthode prend plus d’un argument — et systématiquement pour du code généré, qui n’a personne pour relire ses appels.
À vous
- Retirez
text:du premier programme et exécutez de nouveau : la version positionnelle fait exactement la même chose. - Dans le second, échangez les deux valeurs du troisième appel :
self.saluer("Magus", "Simon"). Le programme tourne, et salue quelqu’un d’autre — pour s’en apercevoir en lisant l’appel, il faut se rappeler l’ordre des paramètres desaluer. Faites la même erreur dans le premier appel,prenom: "Magus", nom: "Simon": elle saute aux yeux, sans rien relire. C’est ce qu’un appel nommé achète. - Écrivez
Console.print(texte: "…")— un nom de paramètre qui n’existe pas — et lisez la réponse du compilateur : deux diagnostics,T022puisT024. Pourquoi deux ?
Commentaires et documentation
Ce que le compilateur jette, ce qu'il retient — et à qui ça sert.
Le programme du début, inchangé dans ce qu’il fait, mais annoté.
@doc("Le premier programme du cours : il salue les archonites.")
@entry(demarrer)
object MonProgramme {
// La méthode que @entry désigne : c'est ici que le programme commence.
@doc("Écrit la salutation sur la sortie standard.")
demarrer() {
// Une ligne de texte, et le programme s'arrête.
Console.print("Salutations archonites !");
}
}
Exécutez-le : la sortie est exactement celle du début de la section. Ni les commentaires ni
@doc ne changent ce que le programme fait. Ils changent ce qu’on peut en lire.
// — un commentaire de ligne
Tout ce qui suit // jusqu’à la fin de la ligne est ignoré par le compilateur. Un commentaire
ne vit que dans le fichier source ; rien ne va le rechercher ailleurs.
/* … */ — un commentaire de bloc, qui s’imbrique
La seconde forme court sur autant de lignes qu’il faut, et elle s’imbrique : un /*
intérieur ouvre un niveau, un */ en referme un, le commentaire finit quand le compte revient
à zéro.
@doc("Le premier programme du cours : il salue les archonites.")
@entry(demarrer)
object MonProgramme {
@doc("Écrit la salutation sur la sortie standard.")
demarrer() {
/* Mis de côté le temps d'un essai :
Console.print("Bonjour tout le monde !");
/* on avait noté ici pourquoi cette ligne existait */
fin de la mise de côté */
Console.print("Salutations archonites !");
}
}
Exécutez : même sortie encore. Le bloc mis de côté contient lui-même un commentaire, et ça ne
le termine pas prématurément — c’est précisément ce que l’imbrication achète. Dans un langage
qui suit la règle du C, le premier */ referme : la ligne fin de la mise de côté */ redevient
du code, et vous récoltez une erreur de syntaxe qui ne parle de rien. Archon compte les niveaux
plutôt que de choisir en silence.
Une ouverture jamais refermée est une erreur, L010, signalée à la position du /* — là
où vous devez aller corriger, pas à la fin du fichier où le compilateur s’en aperçoit.
@doc("…") — une description attachée à la déclaration
@doc est une directive, comme @entry : elle se pose au-dessus d’une déclaration et lui
attache un texte. Il y en a deux dans le programme — une sur l’object, une sur la méthode —
et @doc s’applique à n’importe quelle déclaration : un type, une méthode, un champ, une
constante.
La différence avec un commentaire n’est pas le ton, c’est la prise. Un commentaire flotte à
côté du code, à la ligne où on l’a laissé ; @doc est accroché à une déclaration précise, et
le compilateur sait laquelle.
Ce n’est pas une commande magique
@doc n’affiche rien, ne vérifie rien, ne change pas le programme d’un iota. Elle range une
description à un endroit où les outils savent aller la chercher — et c’est tout ce qu’on lui
demande.
La différence est là. Un outil qui veut dire ce que fait demarrer n’a rien à deviner : il n’a
pas à choisir lequel des commentaires d’alentour s’y rapporte, ni à espérer qu’un document rangé
ailleurs soit encore à jour. La description est accrochée à la déclaration, et le compilateur
sait laquelle. Et une IA qui lit votre code — pour vous aider à l’écrire, à le relire, à le
corriger — y gagne de la même façon : l’intention est là, à sa place, au lieu d’être à deviner.
Une variante existe, où le texte vit dans un fichier séparé plutôt que dans le code
(@doc(file: …, label: …)) — utile quand la description est longue. On la verra plus tard ;
ici, le texte est dans le code, sous les yeux.
Le piège du commentaire, et ce qu’@doc en retire
Un commentaire est utile, et c’est justement pour ça qu’il est dangereux. Le compilateur ne le lit pas : il ne vous dira jamais qu’il est devenu faux. Un code qu’on modifie sans toucher au commentaire d’à côté finit par mentir avec autorité — le lecteur croit la phrase, elle a l’air d’avoir été écrite exprès, et il lui faut plus de temps pour comprendre le code qu’il ne lui en aurait fallu sans aucun commentaire. Le bloc aggrave le risque, parce qu’il en cache beaucoup d’un coup, et qu’on le relit d’autant moins qu’il est long.
D’où la question à se poser devant chaque commentaire qu’on s’apprête à écrire : est-ce qu’il
décrit une déclaration ? Si oui — ce que fait cette méthode, ce que contient ce champ, à quoi
sert ce type —, sa place est @doc, accroché à elle. Il y reste au bon endroit quand le code
bouge autour, un outil sait le retrouver, et le renommage de la déclaration ne le laisse pas
orphelin. Dans un langage classique, c’est une grosse part de ce qu’on écrit en commentaire.
Ce qui reste au commentaire, c’est ce qui ne décrit aucune déclaration : une note sur une astuce au milieu d’un corps de méthode, une raison de faire autrement qu’attendu, une région mise de côté le temps d’un essai — le cas du bloc, plus haut.
Le témoin le plus net est le compilateur d’Archon lui-même : il n’y a pas un seul commentaire
dans ses fichiers .arc, et un contrôle automatique refuse le dépôt si quelqu’un en ajoute
un. Tout est dans @doc.
À vous
- Ajoutez un
//de votre cru, et exécutez : rien ne bouge. - Dans le second exemple, retirez le
*/de la ligne « fin de la mise de côté » et exécutez. Le compilateur vous répondL010, et il vous pointe le/*qui manque de fermeture — pas la fin du fichier. - Changez le texte d’un
@docet exécutez. La sortie ne change pas : c’est précisément le point.