Cours Lua

Formation Lua Programmation en PDF


Télécharger Formation Lua Programmation en PDF

★★★★★★★★★★3.5 étoiles sur 5 basé sur 1 votes.
Votez ce document:

Télécharger aussi :


Document de formation sur Lua Programmation [Eng]

INTRODUCTION

Les langages à typage dynamique tels que Lua évitent les types statiques en faveur des balises de type runtime qui classifient les valeurs qu'ils calculent, et leurs implémentations utilisent ces balises pour effectuer des vérifications d'exécution (ou dynamiques) et garantir que seules les opérations valides sont exécutées [21].

L'absence de types statiques signifie que les programmeurs n'ont pas besoin de s'inquiéter des types d'abstraction qui pourraient nécessiter un système de type complexe et un vérificateur de type à valider, conduisant à des langages et des implémentations plus simples et plus flexibles. Mais cette absence peut également cacher des bogues qui ne seront détectés qu'après le déploiement si les programmeurs ne testent pas correctement leur code.

En revanche, la vérification de type statique aide les programmeurs à détecter de nombreux bogues au cours de la phase de développement. Les types statiques fournissent également un cadre conceptuel qui aide les programmeurs à définir des modules et des interfaces pouvant être combinés pour structurer le développement de grands programmes.

La détection précoce des erreurs et la meilleure structure de programme offerte par la vérification de type statique peuvent amener les programmeurs à migrer leur code d'un langage typé dynamiquement vers un langage typé statiquement, une fois que leurs scripts simples deviennent des programmes complexes [28]. Comme cette migration implique des langages avec une syntaxe et une sémantique différentes, elle nécessite une réécriture complète des programmes existants au lieu d'une évolution incrémentielle des types dynamiques vers les types statiques.

Idéalement, les langages de programmation devraient offrir aux programmeurs la possibilité de choisir entre typage statique et dynamique: les systèmes de type facultatifs [7] et de typage progressif [22] offrent aux programmeurs la possibilité d'utiliser des annotations de type où la saisie statique est nécessaire. système de types dynamiques aux types statiques. La différence entre ces deux approches est la manière dont elles traitent la sémantique d'exécution: alors que les systèmes de type optionnels n'affectent pas la sémantique d'exécution, la typage progressif utilise des contrôles d'exécution pour s'assurer que le code typé dynamiquement ne viole pas les invariants du code statiquement typé.

Cet article présente la conception initiale de Lua typé: un système de type optionnel pour Lua qui est assez complexe pour préserver certains des idiomes auxquels les programmeurs Lua sont déjà habitués, tout en ajoutant de nouvelles constructions qui aident les programmeurs à structurer les programmes Lua.

Lua est un petit langage de script impératif avec des fonctions firstclass (avec une portée lexicale appropriée) où la structure de données principale est la table, un tableau associatif qui peut jouer le rôle de tableaux, enregistrements, cartes, objets, etc. avec du sucre syntaxique et métaprogrammation surcharge de l'opérateur intégré dans la langue. Contrairement à d'autres langages de script, Lua a une coercition très limitée parmi différents types de données.

L'utilisation principale de Lua a toujours été comme un langage intégré pour la configuration et l'extension d'autres applications. Lua préfère fournir des mécanismes plutôt que des politiques fixes pour structurer des programmes, et même des caractéristiques telles qu'un système de modules et une orientation d'objet sont une question de convention au lieu d'être intégrées dans le langage. Le résultat est un écosystème fragmenté de bibliothèques, et différentes idées parmi les programmeurs Lua sur la façon dont ils devraient utiliser les fonctionnalités du langage et comment ils devraient structurer les programmes.

L'absence de politiques standard est un défi pour la conception d'un système de type statique pour la langue Lua. La conception de Lua typée est basée sur une étude (en grande partie automatisée) des idiomes de Lua utilisés dans un grand corpus de bibliothèques Lua, au lieu de se baser uniquement sur la sémantique de la langue.

Typed Lua permet au code Lua typé de coexister et d'interagir avec du code typé dynamiquement. Le compilateur Lued typé avertit le programmeur des erreurs de type, mais génère toujours du code Lua qui s'exécute dans les implémentations Lua non modifiées. Le programmeur peut profiter des avantages des types statiques même sans convertir les modules Lua existants en Typed Lua: un module dynamiquement typé peut exporter une interface typée statiquement, et les utilisateurs statiquement typés du module verront leur utilisation vérifiée par le compilateur .

Contrairement aux systèmes de type progressif, Typed Lua n'introduit pas de contrôles d'exécution entre les parties typées dynamiquement et statiquement du programme. Contrairement à certains systèmes de type optionnels, le sous-ensemble de Typed Lua typé statiquement est basé sur la conception, de sorte qu'une version ultérieure du compilateur Lued typé peut passer d'une saisie progressive à une saisie manuelle.

Nous couvrons les parties principales de la conception, ainsi que leur relation avec Lua, dans les sections 2 à 6. La section 7 passe en revue les travaux relatifs au mélange de typage statique et dynamique dans la même langue. Enfin, la section 8 donne quelques remarques de conclusion et de futures extensions pour Typed Lua.

  1. TYPES ATOMIQUES ET FONCTIONS

Les valeurs Lua peuvent avoir l'une des huit balises: nil, booléen, nombre, chaîne, fonction, table, données utilisateur et thread. Dans cette section, nous verrons comment Typed Lua affecte les types aux valeurs des cinq premiers.

La figure 1 donne la syntaxe abstraite des types typés Lua. Seuls les types de première classe correspondent aux valeurs réelles de Lua; Les types de seconde classe correspondent aux listes d'expressions, et Typed Lua les utilise pour taper plusieurs affectations et applications de fonction.



Les types sont classés par une relation de sous-type, où tout type de première classe est un sous-type de valeur. Le reste de la relation de sous-type est standard: les types d'union sont des supertypes de leurs parties, number, boolean et string sont des supertypes de leurs types littéraux respectifs, les types de fonction sont liés par contravariance sur la partie input et covariance dans la partie output ont un sous-typage de largeur, avec un sous-typage de profondeur sur les champs const, les types tuple et vararg sont covariants.

Le type dynamique any permet au code typé dynamiquement d'interopérer avec du code typé statiquement; c'est un sous-type de valeur, mais ni un supertype ni un sous-type d'un autre type. Nous relions les uns aux autres avec les relations de cohérence et de sous-type cohérent utilisées par les systèmes de type progressif [22, 23]. En pratique, nous pouvons transmettre une valeur du type dynamique chaque fois que nous voulons une valeur d'un autre type, et pouvons passer une valeur où une valeur du type dynamique est attendue, mais ces opérations sont suivies par le système de type, et le programmeur peut choisir d'être averti à leur sujet.

...

Typed Lua permet des annotations de type optionnelles dans les déclarations de fonctions variables. Il affecte le type dynamique à n'importe quel paramètre qui n'a pas d'annotation de type, mais assigne des types plus précis aux variables non annotées, en fonction du type de l'expression qui donne la valeur initiale de la variable.

Dans l'exemple suivant, nous utilisons des annotations de type dans une déclaration de fonction mais n'utilisons pas d'annotations de type dans la déclaration d'une variable locale:

fonction locale factorielle (n: nombre): nombre

si n == 0 alors

retour 1

autre

retour n * factoriel (n - 1)

fin

fin

local x = 5

print (factoriel (x))

Le compilateur affecte le numéro de type à la variable locale x, et cet exemple compile sans avertissements. Typed Lua permet aux programmeurs de combiner du code annoté avec du code non annoté, comme nous le montrons dans l'exemple suivant:

fonction locale abs (n: nombre)

si n <0 alors

retour -n

autre

retourner n

fin

fin

distance de fonction locale (x, y)

retour abs (x - y)

fin

Le compilateur affecte le type dynamique any à l'entrée

paramètres de distance car ils n'ont pas d'annotations de type. La soustraction d'une valeur de type any à une autre donne aussi une valeur de type any (Lua a une surcharge d'opérateur, donc l'opération moins n'est pas assurée de renvoyer un nombre dans ce cas), mais un sous-typage cohérent nous laisse passer une valeur de type any à fonction qui attend un nombre.

Même si les types de retour d'abs et de distance ne sont pas donnés, le compilateur est capable d'inférer un type de numéro de retour aux deux fonctions, car elles sont locales et non récursives.

Lua a des fonctions de première classe, mais ils ont quelques particularités. Premièrement, le nombre d'arguments passés à une fonction n'a pas besoin de correspondre à l'arité de la fonction; Lua supprime silencieusement des arguments supplémentaires après les avoir évalués, ou passe à zéro à la place des arguments manquants. Deuxièmement, les fonctions peuvent renvoyer n'importe quel nombre de valeurs, et le nombre de valeurs renvoyées peut ne pas être connu statiquement. Troisièmement, Lua a aussi plusieurs affectations, et la sémantique du passage des arguments correspond à celles des affectations multiples (ou vice-versa); appeler une fonction revient à faire une assignation multiple où le côté gauche est la liste des paramètres et le côté droit est la liste des arguments.

Typed Lua utilise des types de seconde classe pour coder les particularités du passage d'arguments, des retours multiples et de l'assignation multiple. Nous les appelons de seconde classe parce que ces types ne correspondent pas à des valeurs réelles et ne peuvent pas être assignés à des variables ou paramètres: ils sont un artefact de l'interaction entre le système de types et la sémantique de Lua.

Comme nous pouvons le voir sur la figure 1, un type de seconde classe dans Lua typé peut être un tuple de types de première classe se terminant éventuellement par un type variadique, ou une union de ces tuples. Un type variadique T * est un générateur pour une suite de valeurs de type T U nul. Les unions de tuples jouent un rôle important dans les fonctions qui sont surchargées sur le type de retour, ainsi que les types de projection. Les deux sont expliqués dans la section suivante.

Dans son mode de fonctionnement par défaut, Typed Lua ajoute toujours une queue variadique aux parties d'un type de fonction si aucune n'est spécifiée, pour correspondre à la sémantique des appels de fonction Lua. Dans les exemples ci-dessus, les types factoriels et abs sont en fait numberxvalue * - + numberxnil *, et le type de distance est n'importe quel x any x value * - + number x nil *.

Si nous appelons des abs avec des arguments supplémentaires, Lued Lua les ignore silencieusement, car la signature de type permet à l'abs de recevoir un nombre d'arguments supplémentaires. Si nous appelons abs dans le côté droit d'une affectation à plus d'une lvalue, Lua typé vérifie si le premier lvalue a un type cohérent avec le nombre, et toutes les autres lvalues ​​doivent avoir un type cohérent avec nil.



Il existe un mode d'opération plus strict optionnel où Typed Lua ne donne pas de queues variées aux parties d'un type de fonction à moins que le programmeur ne le déclare explicitement, et vérifie également tous les appels de fonction pour la discordance d'arité.

Un type variadique ne peut apparaître que dans la position de queue d'un tuple, car Lua ne prend que la première valeur de toute expression qui apparaît dans une liste d'expressions qui n'est pas en position de queue. L'exemple suivant montre l'interaction entre plusieurs retours et listes d'expressions:

fonction locale multiple ()

return 2, "foo"

fin

somme des fonctions locales (x: nombre, y: nombre)

retour x + y

fin

local x, y, z = multiple (), multiple ()

print (somme (multiple (), multiple ())

La fonction multiple est value * - + numberxstring xnil *, et sum est numberxnumberxvalue * - + numberxnil *. Dans le côté droit de l'assignation multiple, seule la première valeur produite par le premier appel à multiple est utilisée, de sorte que le type du côté droit est le nombre x nombre x chaîne x nil *, et les types affectés à x, y et z sont respectivement le nombre, le nombre et la chaîne. Cela signifie également que l'appel à sum se compile sans erreurs, car les deux premiers composants du tuple sont cohérents avec les types de paramètres, et les autres composants sont cohérents avec la valeur.

  1. SYNDICATS

Typed Lua utilise des types d'union pour coder certains idiomes courants de Lua: des valeurs optionnelles, une surcharge basée sur les balises des paramètres d'entrée, et une surcharge sur le type de retour des fonctions.

Les valeurs optionnelles sont des unions de type et de zéro, et sont si courantes que Typed Lua utilise le t? syntaxe pour ces unions. Ils apparaissent chaque fois qu'une fonction a des paramètres facultatifs et chaque fois que le programme lit une valeur dans un tableau ou une carte.

message de fonction locale (nom: chaîne,

salutation: chaîne?)

salut local = salutation ou "bonjour" retour salutation .. nom

fin

imprimer (message ("Lua"))

print (message ("Lua", "Salut"))

Dans cet exemple, le second paramètre est facultatif mais, dans la première ligne de la fonction, nous déclarons une nouvelle variable dont la chaîne de caractères est garantie à la place de la chaîne U nil. Dans Lua, toutes les valeurs sauf nil et false sont «truey», de sorte que le court-circuit ou l'opérateur est un moyen courant de donner une valeur par défaut à un paramètre optionnel. Typed Lua code cet idiome avec une règle de typage: si le côté gauche ou a le type T U nil et le côté droit a le type T alors l'expression ou a le type T.

La déclaration d'une nouvelle variable d'accueil qui masque le paramètre n'est pas nécessaire:

message de fonction locale (nom: chaîne,

salutation: chaîne?)

salutation = salutation ou "Bonjour"

retour salutation .. nom

fin

Le type Lua permet à l'assignation x = x ou e de changer le type de x de t U nil à t, tant que le type de e est un sous-type de t, x est local à la fonction courante, et non affecté dans une autre fonction. La modification affecte uniquement le type de x dans le reste de la portée actuelle. Dans le cas d'un message d'accueil, l'affectation sur la ligne trois change son type en chaîne.

Les fonctions surchargées utilisent la fonction type pour inspecter l'étiquette de leurs paramètres, et effectuent différentes actions en fonction de ce que ces étiquettes sont. Le cas le plus simple surcharge un seul paramètre:

surcharge de fonction locale (s1: chaîne,

s2: string | nombre)

si type (s2) == "chaîne" alors

return s1 .. s2

autre

- string.rep: (chaîne, nombre) -> chaîne

return string.rep (s1, s2)

fin

fin

Typed Lua a un petit ensemble de prédicats de type qui, lorsqu'il est utilisé sur une variable locale dans une condition, contraint le type de cette variable. La fonction ci-dessus utilise le prédicat type (x) == "chaîne" qui contraint le type de x de chaîne T U à chaîne lorsque le prédicat est vrai et T sinon. C'est une forme simplifiée de typage de flux [13, 30]. Comme avec ou, la variable doit être locale à la fonction et ne peut pas être affectée à une autre fonction.

Les prédicats de type peuvent seulement discriminer en fonction des étiquettes, donc ils sont limités sur les types d'unions qu'ils peuvent discriminer. Il est possible de discriminer une union qui combine un type de table avec un type de base, ou un type de table avec un type de fonction, ou deux types de base, mais il n'est pas possible de distinguer deux types de fonctions différents.

Les fonctions qui surchargent leurs types de retour pour signaler l'apparition d'erreurs sont un autre idiome courant de Lua. Dans cette idiome, une fonction renvoie son ensemble normal de valeurs de retour en cas de succès, mais si quelque chose échoue, renvoie nil comme première valeur, suivi d'un message d'erreur ou d'autres données décrivant l'erreur, comme dans l'exemple suivant:



idiv de fonction locale (d1: nombre, d2: nombre):

(nombre, nombre) | (néant, chaîne)

si d2 == 0 alors

retour nul, "division par zéro"

autre

local r = d1% d2

q = (d1 - r) / d2 local

return q, r

fin

fin

Il y a aussi une syntaxe spéciale pour cet idiome: nous pourrions annoter le type de retour de idiv avec (nombre, nombre)? pour désigner la même union1.

Le type complet de idiv est le nombre x nombre x valeur * - + (nombrexnumberxnil *) U (nilxstringxnil *). Un client typique de cette fonction l'utiliserait comme suit:

q local, r = idiv (n1, n2)

- q est le nombre | nil, r est le nombre | la chaîne si q alors

- q et r sont des nombres

- r est une fin de chaîne

Lorsque typé Lua rencontre une union de tuples dans le côté droit d'une déclaration, il stocke l'union dans un environnement de type spécial avec un nouveau nom et assigne les types de projection aux variables dans le côté gauche de la déclaration. Si la variable de type est X, la variable q obtient le type X1 et la variable r le type X2.

Si nous devons vérifier un type de projection par rapport à un autre type, nous prenons l'union du composant correspondant dans chaque tuple. Mais si une variable avec un type de projection apparaît dans un prédicat de type, le prédicat discriminera tous les tuples dans l'union. Dans l'exemple ci-dessus, X est (nombre x nombre x nil *) U (nul x chaîne x nil *) en dehors de l'instruction if, mais nombre x nombre x nil * dans le bloc then et nil x string x nil * dans le autre bloc.

Notez que nous pourrions aussi discriminer r en utilisant type (r) == "nombre" comme notre prédicat, avec le même résultat. La première forme est plus succincte et plus idiomatique. Nous pouvons également utiliser des types de projection pour écrire des fonctions surchargées où le type d'un paramètre dépend du type d'un autre paramètre.

L'affectation à une variable avec un type de projection est interdite, sauf si l'union a été discriminée jusqu'à un seul tuple, l'assignation sans restriction à ces variables serait malsaine, car elle pourrait casser la relation de dépendance entre les types dans chaque tuple qui fait partie du syndicat.

Actuellement, une limitation de nos mécanismes de surcharge est que le type de retour ne peut pas dépendre des types d'entrée; nous ne pouvons pas écrire une fonction qui soit sûre de renvoyer un nombre si un nombre est passé et garanti de renvoyer une chaîne si une chaîne est passée, par exemple. Alors que les types d'intersection fournissent un moyen d'exprimer le type d'une fonction comme nombre - + nombre n chaîne - + chaîne, un typage de flux plus sophistiqué est nécessaire pour vérifier qu'une fonction possède ce type, et nous travaillons toujours sur ce problème.

  1. TABLES ET INTERFACES

Les tables sont le mécanisme principal que Lua a pour construire des structures de données. Ce sont des tableaux associatifs où n'importe quelle valeur (sauf nil) peut être une clé, mais avec un support de langage pour utiliser efficacement les tables comme des tuples, des tableaux (denses ou clairsemés), des enregistrements, des modules et des objets. Dans cette section, nous montrons comment Typed Lua code les tableaux sous forme de tableaux, d'enregistrements, de tuples et de cartes simples dans son système de types.

Typed Lua utilise le même framework pour représenter les différentes utilisations d'une table Lua: les types de table. Un type de table {t1: u1, ..., tn: un} représente une carte de valeurs de type ti à des valeurs de type ui.

La syntaxe concrète de Lua typé a une syntaxe pour les types de tables communes. Une syntaxe définit les types de table pour les cartes: elle est écrite {t: u} et correspond au type de table {t: u}. Ce type de table représente une carte qui mappe les valeurs de type t aux valeurs de type u. Une autre syntaxe définit les types de table pour les tableaux: elle est écrite {t} et est équivalente au type de table {number: t}. Une troisième syntaxe définit les types de table pour les enregistrements: il est écrit {s1: t1, ..., sn: tn}, où chaque si est un nombre littéral, une chaîne, ou un booléen, et correspond au type de table {s1: t1, ..., sn: tn}, où chaque si est le type littéral correspondant.



337