web-dev-qa-db-fra.com

Une variante LISP complète de type statique est-elle possible?

Une variante LISP complète de type statique est-elle possible? Est-il même logique que quelque chose comme ça existe? Je crois que l'une des vertus d'un langage LISP est la simplicité de sa définition. Le typage statique compromettrait-il ce principe de base?

101

Oui, c'est très possible, bien qu'un système de type HM standard soit généralement le mauvais choix pour la plupart des codes LISP/Scheme idiomatiques. Voir Typed Racket pour un langage récent qui est un "Full LISP" (plus comme Scheme, en fait) avec un typage statique.

55
Eli Barzilay

Si tout ce que vous vouliez était un langage de type statique qui ressemblait à LISP, vous pourriez le faire assez facilement, en définissant un arbre de syntaxe abstrait qui représente votre langue et puis mapper cela AST aux expressions S. Cependant, je ne pense pas que j'appellerais le résultat LISP.

Si vous voulez quelque chose qui a réellement des caractéristiques LISP-y en plus de la syntaxe, il est possible de le faire avec un langage typé statiquement. Cependant, il existe de nombreuses caractéristiques de LISP dont il est difficile d'obtenir une saisie statique très utile. Pour illustrer cela, jetons un coup d'œil à la structure de liste elle-même, appelée contre , qui constitue le bloc de construction principal de LISP.

Appeler les inconvénients une liste, même si (1 2 3) En ressemble à une, est un peu inapproprié. Par exemple, ce n'est pas du tout comparable à une liste de type statique, comme std::list De C++ ou la liste de Haskell. Ce sont des listes liées unidimensionnelles où toutes les cellules sont du même type. LISP permet heureusement (1 "abc" #\d 'foo). De plus, même si vous étendez vos listes de type statique pour couvrir des listes de listes, le type de ces objets nécessite que chaque élément de la liste soit une sous-liste. Comment représenteriez-vous ((1 2) 3 4) En eux?

LISP conses forme un arbre binaire, avec des feuilles (atomes) et des branches (conses). De plus, les feuilles d'un tel arbre peuvent contenir n'importe quel type atomique (non contre-courant) de LISP! La flexibilité de cette structure est ce qui rend LISP si bon à gérer le calcul symbolique, les AST et à transformer le code LISP lui-même!

Alors, comment modéliseriez-vous une telle structure dans un langage typé statiquement? Essayons-le dans Haskell, qui a un système de type statique extrêmement puissant et précis:

type Symbol = String
data Atom = ASymbol Symbol | AInt Int | AString String | Nil
data Cons = CCons Cons Cons 
            | CAtom Atom

Votre premier problème va être la portée du type Atom. De toute évidence, nous n'avons pas choisi un type de flexibilité suffisante pour couvrir tous les types Atom) d'objets que nous voulons écarter par contre. Au lieu d'essayer d'étendre la structure de données Atom comme indiqué ci-dessus (que vous pouvez clairement voir fragile), disons que nous avions une classe de type magique Atomic qui distinguait tous les types que nous voulions rendre atomiques. Ensuite, nous pourrions essayer:

class Atomic a where ?????
data Atomic a => Cons a = CCons Cons Cons 
                          | CAtom a

Mais cela ne fonctionnera pas car cela nécessite que tous les atomes de l'arbre soient du même type . Nous voulons qu'ils puissent différer d'une feuille à l'autre. Une meilleure approche nécessite l'utilisation des quantificateurs existentiels de Haskell :

class Atomic a where ?????
data Cons = CCons Cons Cons 
            | forall a. Atomic a => CAtom a 

Mais maintenant, vous arrivez au cœur du problème. Que pouvez-vous faire avec des atomes dans ce type de structure? Quelle structure ont-ils en commun qui pourrait être modélisée avec Atomic a? Quel niveau de sécurité de type êtes-vous garanti avec un tel type? Notez que nous n'avons ajouté aucune fonction à notre classe de type, et il y a une bonne raison: les atomes ne partagent rien en commun dans LISP. Leur supertype dans LISP est simplement appelé t (c'est-à-dire en haut).

Pour les utiliser, vous devez trouver des mécanismes pour contraindre dynamiquement la valeur d'un atom à quelque chose que vous pouvez réellement utiliser. Et à ce stade, vous avez essentiellement implémenté un sous-système à typage dynamique dans votre langage à typage statique! (On ne peut s'empêcher de noter un possible corollaire à la dixième règle de programmation de Greenspun . )

Notez que Haskell prend en charge un tel sous-système dynamique avec un type Obj, utilisé conjointement avec un type Dynamic et un classe typable = pour remplacer notre classe Atomic, qui permet de stocker des valeurs arbitraires avec leurs types, et une coercition explicite à partir de ces types. C'est le genre de système que vous devez utiliser pour travailler avec les structures contre LISP dans leur généralité complète.

Ce que vous pouvez également faire est d'aller dans l'autre sens et d'incorporer un sous-système à typage statique dans un langage typé dynamiquement. Cela vous permet de bénéficier d'une vérification de type statique pour les parties de votre programme qui peuvent tirer parti d'exigences de type plus strictes. Cela semble être l'approche adoptée dans la forme limitée de CMUCL vérification de type précise , par exemple.

Enfin, il existe la possibilité d'avoir deux sous-systèmes distincts, typés dynamiquement et statiquement, qui utilisent une programmation de type contrat pour faciliter la transition entre les deux. De cette façon, le langage peut prendre en charge les utilisations de LISP où la vérification de type statique serait plus un obstacle qu'une aide, ainsi que les utilisations où la vérification de type statique serait avantageuse. C'est l'approche adoptée par Typed Racket , comme vous le verrez dans les commentaires qui suivent.

32
Owen S.

Ma réponse, sans un haut degré de confiance est probablement . Si vous regardez un langage comme SML, par exemple, et que vous le comparez avec LISP, le noyau fonctionnel de chacun est presque identique. En conséquence, il ne semble pas que vous auriez beaucoup de mal à appliquer une sorte de typage statique au cœur de LISP (application de fonction et valeurs primitives).

Cependant, votre question dit complet , et où je vois une partie du problème, c'est l'approche du code en tant que données. Les types existent à un niveau plus abstrait que les expressions. LISP n'a pas cette distinction - tout est "plat" dans sa structure. Si nous considérons une expression E: T (où T est une représentation de son type), puis que nous considérons cette expression comme étant de simples données, alors quel est exactement le type de T ici? Eh bien, c'est une sorte! Un type est un type d'ordre supérieur, alors allons-y et disons quelque chose à ce sujet dans notre code:

E : T :: K

Vous pourriez voir où je veux en venir. Je suis sûr qu'en séparant les informations de type du code, il serait possible d'éviter ce type d'auto-référentialité des types, mais cela rendrait les types peu "LISP" dans leur saveur. Il y a probablement plusieurs façons de contourner ce problème, mais ce n'est pas évident pour moi qui serait le meilleur.

EDIT: Oh, donc avec un peu de recherche sur Google, j'ai trouvé Qi , qui semble être très similaire à LISP, sauf qu'il est typé statiquement. C'est peut-être un bon endroit pour commencer à voir où ils ont apporté des modifications pour y introduire le typage statique.

10
Gian
4
Rainer Joswig