Comment un système de type statique affecte-t-il la conception d'un langage basé sur un prototype?

15

L' article de Wikipedia sur les langues basées sur des prototypes contient le paragraphe suivant:

Presque tous les systèmes basés sur des prototypes sont basés sur des langages interprétés et typés dynamiquement. Cependant, des systèmes basés sur des langages typés statiquement sont techniquement réalisables.

De quelles manières un système de type statique impose-t-il des restrictions ou introduit-il de la complexité dans un langage basé sur des prototypes, et pourquoi existe-t-il des langages de prototypes plus typés dynamiquement?

Joe
la source
2
+1 et favori: j'y réfléchis moi-même depuis un bon moment, et je n'ai trouvé aucun problème extrêmement difficile avec un système de type structurel . En fait, cela me dérange tellement que je veux aller de l'avant et essayer de créer un langage basé sur un prototype de type statique juste pour voir quels problèmes il y a ...
Je commence ce processus moi-même pour la même raison :)
Joe

Réponses:

6

La frontière entre un type fondamental et un objet est floue et souvent introduite artificiellement. Par exemple, en C, une structure n'est qu'un groupe d'enregistrements, juste un type non-objet dérivé. En C ++, une structure est une classe avec tous les champs publics, un objet. Pourtant, C ++ est presque totalement rétrocompatible avec C ... la frontière est vraiment douce ici.

Pour la programmation basée sur des prototypes, vous devez avoir des objets modifiables au moment de l'exécution. Ils DOIVENT être typés en douceur car chacun change au moment de l'exécution, une classe d'un type se change en un autre - son type change.

Vous pouvez cependant conserver les types non-objets fondamentaux et dérivés comme statiques. Mais cela introduit une disparité étrange, les objets sont de type doux, les non-objets sont de type statique et une barrière rigide doit être établie entre les deux. Devriez-vous être capable de transformer une structure? Un string? Number devrait-il être une classe ou un type fondamental, ou un ensemble de types fondamentaux, int / float / bignum / etc?

Il est juste plus naturel et facile à apprendre, à utiliser et à écrire pour avoir cet uniforme, tous les types sont mutables ou aucun type n'est mutable au moment de l'exécution. Si vous déclarez qu'un seul type (objet) est mutable, vous vous retrouvez avec des maux de tête et des problèmes des deux mondes.

Le type statique est:

  • plus facile à mettre en œuvre
  • plus rapide / plus efficace
  • plus sûr
  • plus facile à entretenir / documenter les gros systèmes grâce à l'abstraction.

Le typage dynamique est:

  • plus rapide à écrire,
  • plus concis
  • langue plus facile à apprendre
  • plus indulgent pour les erreurs de conception.

En mélangeant les deux, vous sacrifiez beaucoup.

  • La mise en œuvre devient plus difficile que n'importe laquelle des deux précédentes.
  • la vitesse dépend si vous utilisez les types doux ou non ... Si vous le faites, c'est faible, si vous ne le faites pas, pourquoi choisir la langue?
  • la sécurité de type est hors de la fenêtre pour tous les types d'objets.
  • suivre comment un type se transforme en un autre est une tâche assez difficile. Le documenter - très dur.
  • Vous devez toujours faire toute la comptabilité avec des types fondamentaux, ce qui tue la concision et la vitesse d'écriture
  • La complexité du langage est plus élevée (plus difficile à apprendre) que n'importe laquelle des langues "spécifiques",
  • le "pardon" d'un type dynamique est remplacé par une tendance à des erreurs très délicates au niveau des types d'attributs incompatibles.
SF.
la source
1
Pensez à fournir un exemple de la raison pour laquelle les objets doivent être "mutables" (je suppose que vous entendez l'ajout et la suppression d'attributs, et non leur modification, car cela n'est généralement pas lié à la saisie).
@delnan: pas vraiment, dans la programmation basée sur des prototypes, vous pouvez creuser les entrailles d'un objet comme bon vous semble, supprimer, ajouter, remplacer, couvrir, les deux méthodes et attributs sur une instance en direct. C'est tout l'intérêt et un remplacement très pratique et flexible pour modifier des objets à travers des règles rigides d'héritage classique. Si un langage utilise une classe comme type, vous ne pouvez pas modifier sa structure à la volée si le type n'est pas souple.
SF.
1
Je ne pense pas. Selon la même logique, on pourrait soutenir que la programmation basée sur les classes a besoin des mêmes libertés que les langages dynamiques basés sur les classes le permettent. Non, les prototypes statiques avec des systèmes de type structurel annoteraient des objets avec une liste de ses membres et récursivement leurs types, et vérifieraient statiquement que ces membres existent (et ont le bon type) en exigeant que tous les membres soient donnés lors de la création d'objet ou présents dans le prototype et tout simplement pas inclure un moyen de supprimer des membres. Le résultat me semble encore assez prototypique et garantit que chaque membre est présent à tout moment.
@delnan: Vous venez de décrire l'héritage classique par la composition. Oui, il semble assez prototypique et est un moyen (très nerf) de faire de la programmation basée sur des prototypes dans un langage de modèle d'héritage classique. Il supprime juste pb.p de 90% du plaisir, tuant ses plus grands avantages (et éliminant simultanément les plus grands dangers). Oui, dans l'ancienne analogie de la prise de pieds, pb.p complet vous aidera à tirer sur vos deux jambes avec une cuillère à thé. Si vous n'aimez pas ce genre de pouvoir, vous feriez mieux de vous en tenir à l'héritage classique.
SF.
1
Vous confondez "dynamique" avec "prototypes". Ces libertés qui ne se marient pas bien avec les systèmes de type statiques ne sont pas des caractéristiques des prototypes, elles sont des caractéristiques du dynamisme. Bien sûr, l'ajout de typage statique les empêche, mais ils ne font pas partie des prototypes (c'est IMGO principalement le manque de classes en faveur du clonage d'objets pour agir en tant que parents). Ce dynamisme est orthogonal aux prototypes. Tous les langages de prototypes populaires les incluent, mais ils sont indépendants des prototypes comme indiqué précédemment. Considérez cet extrait dans un langage fictif: pastebin.com/9pLuAu9F . Comment n'est-ce pas des prototypes?
3

La difficulté est assez simple à voir: En considérant les objets comme des dictionnaires de méthodes ou comme des choses qui répondent aux messages, observez ce qui suit à propos des langages OO typés statiquement:

  • Toutes les clés / messages du dictionnaire sont généralement déclarés à l'avance, en utilisant des identifiants déclarés statiquement.

  • Certains ensembles de messages sont déclarés à l'avance et des objets sont associés à ces ensembles pour déterminer à quels messages ils répondent.

  • Les relations d'inclusion d'un ensemble de messages constituant un sous-ensemble d'un autre sont déclarées de manière statique et explicite; les sous-ensembles non déclarés mais logiques ne sont pas valides.

  • La vérification de type tente de garantir que tous les messages ne sont envoyés qu'aux objets qui y répondent.

Chacun de ces conflits entre dans une certaine mesure avec un système basé sur un prototype:

  • Les noms des messages pourraient être déclarés à l'avance, sous la forme d '"atomes" ou de chaînes internes ou autres, mais pas grand-chose d'autre; la plasticité des objets signifie que l'attribution de types aux méthodes est délicate.

  • C'est sans doute la caractéristique essentielle d'un système basé sur un prototype que les ensembles de messages sont définis par la réponse d'un objet, plutôt que l'inverse. Il serait raisonnable d'attribuer des alias à des combinaisons particulières au moment de la compilation, mais les ensembles de messages déterminés au moment de l'exécution doivent être possibles.

  • L'impact réel des deux ci-dessus frappe les relations d'inclusion, où les déclarations explicites sont complètement irréalisables. L'hérédité dans le sens de sous-typage statique et nominal est antithétique à un système basé sur un prototype.

Ce qui nous amène au dernier point, que nous ne voulons pas réellement changer. Nous souhaitons toujours nous assurer que les messages ne sont envoyés qu'aux objets qui y répondent. Pourtant:

  • Nous ne pouvons pas savoir statiquement quels messages peuvent être regroupés.
  • Nous ne pouvons pas savoir quels regroupements sont des sous-ensembles d'autres.
  • Nous ne pouvons pas savoir quels regroupements sont possibles.
  • Nous ne pouvons même pas spécifier quel type d'arguments sont envoyés avec un seul message.
  • Fondamentalement, nous avons constaté que nous ne pouvons pas spécifier grand-chose du tout dans le cas entièrement général.

Alors, comment cela peut-il être résolu? Soit d'une manière ou d'une autre limiter la généralité complète (ce qui est désagréable et peut rapidement tuer tous les avantages de l'utilisation d'un système basé sur un prototype), soit rendre le système de types beaucoup plus fluide et exprimer des contraintes plutôt que des types exacts .

Le système de type basé sur des contraintes conduit rapidement à la notion de sous-typage structurel , qui dans un sens très vague peut être considéré comme l'équivalent statique du "typage de canard". Les plus grands obstacles ici sont que ces systèmes sont beaucoup plus compliqués à vérifier par type et sont moins bien connus (ce qui signifie peu de travail préalable à étudier).

En résumé: c'est possible, c'est juste plus difficile à faire qu'un système de type statique nominal ou un système dynamique basé sur des métadonnées d'exécution, et donc peu de gens s'en soucient.

CA McCann
la source
1

Je crois qu'un moyen de parvenir à un langage basé sur des prototypes de type statique serait de baser le langage sur des modèles et des concepts.

Les concepts étaient autrefois une fonctionnalité planifiée pour C ++ 0x. Le code générique dans les modèles C ++ est déjà de facto "typiquement duck-typed". L'idée de Concepts est de pouvoir dire certaines choses sur les membres requis et les caractéristiques des types, sans exiger ou impliquer un modèle d'héritage de classe sous-jacent à cette relation (car il devait fonctionner avec un code de modèle existant qui était déjà "typiquement canard typé" ).

Dans un langage basé sur la base des modèles et concepts, ce seraient les concepts basés sur des prototypes, et les modèles vous libéreraient de vous soucier de tout modèle de classe qui peut ou non être utilisé pour implémenter les types de valeurs.

Mis à part les astuces d'utilisation de la compilation par étapes pour permettre au langage d'être son propre méta-langage, ces dérivations prototypiques des concepts seraient nécessairement immuables une fois créées. Cependant, l'objection selon laquelle ce n'est pas basé sur un prototype est un argument redoutable. Ce serait simplement un langage fonctionnel. Un langage de base de prototype dynamique qui est également fonctionnel a au moins été tenté .

Dennis Ferron
la source