Quelles fonctionnalités permettent le typage dynamique? [fermé]

91

J'utilise Python depuis quelques jours maintenant et je pense comprendre la différence entre le typage dynamique et statique. Ce que je ne comprends pas, c’est dans quelles circonstances on le préférerait. Il est flexible et lisible, mais au détriment de davantage de contrôles d’exécution et de tests unitaires supplémentaires requis.

Outre les critères non fonctionnels tels que la flexibilité et la lisibilité, quelles sont les raisons pour choisir le typage dynamique? Que puis-je faire avec la frappe dynamique qui n'est pas possible autrement? Quel exemple de code spécifique pouvez-vous imaginer qui illustre un avantage concret du typage dynamique?

Justin984
la source
5
Théoriquement, vous ne pouvez rien faire non plus, tant que les langues sont Turing Complete . La question la plus intéressante pour moi est de savoir ce qui est facile ou naturel chez l’un par rapport à l’autre. Il y a des choses que je fais régulièrement en Python que je ne considérerais même pas en C ++, même si je sais que c'est capable.
Mark Ransom
28
Comme le dit Chris Smith dans son excellent essai Ce qu’il faut savoir avant de débattre des systèmes de types : "Le problème, dans ce cas, est que la plupart des programmeurs ont une expérience limitée et n’ont pas essayé beaucoup de langages. Pour le contexte, ici, six ou sept ne compte pas comme "beaucoup." ... Deux conséquences intéressantes sont: (1) De nombreux programmeurs ont utilisé de très mauvais langages typés statiquement. (2) De nombreux programmeurs ont très mal utilisé des langages dynamiquement typés. "
Daniel Pryden
3
@suslik: Si les primitives de langage ont des types non sensés, vous pouvez bien sûr faire des choses absurdes avec des types. Cela n’a rien à voir avec la différence entre le typage statique et dynamique.
Jon Purdy
10
@CarekTomczak: C'est une caractéristique de certains langages à typage dynamique, oui. Mais il est possible qu'un langage à typage statique soit modifiable à l'exécution. Par exemple, Visual Studio vous permet de réécrire le code C # lorsque vous êtes à un point d'arrêt dans le débogueur, et même de rembobiner le pointeur d'instruction pour réexécuter votre code avec les nouvelles modifications. Comme j'ai cité Chris Smith dans mon autre commentaire: "De nombreux programmeurs ont utilisé de très mauvais langages typés statiquement" - ne jugez pas tous les langages statiquement typés par ceux que vous connaissez.
Daniel Pryden
11
@WarrenP: Vous affirmez que "les systèmes de types dynamiques réduisent la quantité de données supplémentaires que je dois taper", mais ensuite vous comparez Python à C ++. Ce n'est pas une comparaison juste: bien sûr, C ++ est plus bavard que Python, mais ce n'est pas à cause de la différence de leurs systèmes de types, mais à cause de la différence de leurs grammaires. Si vous souhaitez simplement réduire le nombre de caractères de votre source de programme, découvrez J ou APL: je vous garantis qu'ils seront plus courts. Une comparaison plus juste consisterait à comparer Python à Haskell. (Pour mémoire, j'adore Python et je le préfère au C ++, mais j'aime encore plus Haskell.)
Daniel Pryden

Réponses:

50

Puisque vous avez demandé un exemple spécifique, je vais vous en donner un.

Massive ORM de Rob Conery est de 400 lignes de code. C'est aussi petit parce que Rob est capable de mapper des tables SQL et de fournir des résultats d'objet sans nécessiter beaucoup de types statiques pour mettre en miroir les tables SQL. Ceci est accompli en utilisant le dynamictype de données en C #. La page Web de Rob décrit ce processus en détail, mais il semble clair que, dans ce cas d'utilisation particulier, le typage dynamique est en grande partie responsable de la brièveté du code.

Comparez avec Dapper de Sam Saffron , qui utilise des types statiques; la SQLMapperclasse à elle seule est constituée de 3000 lignes de code.

Notez que les exclusions habituelles s'appliquent et que votre kilométrage peut varier. Dapper a des objectifs différents de ceux de Massive. Je souligne juste ceci comme un exemple de quelque chose que vous pouvez faire dans 400 lignes de code, ce qui ne serait probablement pas possible sans frappe dynamique.


Le typage dynamique vous permet de reporter vos décisions de type à l'exécution. C'est tout.

Que vous utilisiez un langage à typage dynamique ou un langage à typage statique, vos choix de type doivent toujours être judicieux. Vous n'allez pas ajouter deux chaînes ensemble et attendre une réponse numérique à moins que les chaînes ne contiennent des données numériques, et si elles ne le font pas, vous obtiendrez des résultats inattendus. Un langage typé statiquement ne vous laissera pas faire cela en premier lieu.

Les partisans des langages de type statique indiquent que le compilateur peut effectuer une quantité substantielle de "vérification de l'intégrité" de votre code au moment de la compilation, avant qu'une seule ligne ne s'exécute. C'est une bonne chose ™.

C # a le dynamicmot - clé, ce qui vous permet de reporter la décision de type à l'exécution sans perdre les avantages de la sécurité de type statique dans le reste de votre code. L'inférence de type ( var) élimine une grande partie de la douleur liée à l'écriture dans un langage de type statique en supprimant la nécessité de toujours déclarer explicitement les types.


Les langages dynamiques semblent favoriser une approche plus interactive et immédiate de la programmation. Personne ne s'attend à ce que vous deviez écrire une classe et effectuer un cycle de compilation pour taper un peu de code Lisp et le regarder s'exécuter. Pourtant, c'est exactement ce que je suis censé faire en C #.

Robert Harvey
la source
22
Si j'ai ajouté deux chaînes numériques ensemble, je ne m'attendrais toujours pas à un résultat numérique.
pdr
22
@ Robert Je suis d'accord avec la plupart de votre réponse. Cependant, notez qu'il existe des langages à typage statique avec des boucles interactives lecture-évaluation, telles que Scala et Haskell. Il se peut que C # ne soit simplement pas un langage particulièrement interactif.
Andres F.
14
Je dois apprendre un Haskell.
Robert Harvey
7
@RobertHarvey: Vous pourriez être surpris / impressionné par F # si vous ne l'avez pas déjà essayé. Vous obtenez toutes les sécurités de type (à la compilation) que vous avez normalement dans un langage .NET, sauf que vous devez rarement déclarer des types. L'inférence de type en F # va au-delà de ce qui est disponible / fonctionne en C #. Aussi: comme ce que Andres et Daniel font remarquer, F # interactive fait partie de Visual Studio ...
Steven Evers
8
"Vous n'allez pas ajouter deux chaînes ensemble et attendre une réponse numérique à moins que les chaînes ne contiennent des données numériques, et si elles ne le font pas, vous obtiendrez des résultats inattendus" désolé, cela n'a rien à voir avec le typage dynamique vs statique , ceci est fort vs faible typage.
vartec
26

Des expressions telles que "typage statique" et "typage dynamique" sont très répandues et les gens ont tendance à utiliser des définitions légèrement différentes, alors commençons par clarifier ce que nous voulons dire.

Prenons un langage dont les types statiques sont vérifiés au moment de la compilation. Mais disons qu'une erreur de type ne génère qu'un avertissement non fatal et qu'au moment de l'exécution, tout est tapé. Ces types statiques sont uniquement destinés au programmeur et n'affectent pas le codegen. Cela montre que le typage statique n’impose pas en lui-même de limitation et n’exclut pas le typage dynamique. (Objective-C est un peu comme ça.)

Mais la plupart des systèmes de types statiques ne se comportent pas de cette façon. Deux propriétés communes des systèmes de types statiques peuvent imposer des limitations:

Le compilateur peut rejeter un programme contenant une erreur de type statique.

Ceci est une limitation car de nombreux programmes de type sécurisé contiennent nécessairement une erreur de type statique.

Par exemple, j'ai un script Python qui doit fonctionner à la fois en Python 2 et en Python 3. Certaines fonctions ont changé leurs types de paramètres entre Python 2 et 3, j'ai donc un code comme celui-ci:

if sys.version_info[0] == 2:
    wfile.write(txt)
else:
    wfile.write(bytes(txt, 'utf-8'))

Un vérificateur de type statique Python 2 rejetterait le code Python 3 (et vice versa), même s'il ne serait jamais exécuté. Mon programme de type sécurisé contient une erreur de type statique.

Comme autre exemple, considérons un programme Mac qui veut fonctionner sur OS X 10.6, mais tire parti des nouvelles fonctionnalités de 10.7. Les méthodes 10.7 peuvent ou non exister au moment de l'exécution, et c'est à moi, le programmeur, de les détecter. Un vérificateur de type statique est forcé de rejeter mon programme pour garantir la sécurité du type ou de l'accepter, ainsi que de la possibilité de générer une erreur de type (fonction manquante) au moment de l'exécution.

La vérification de type statique suppose que l'environnement d'exécution est correctement décrit par les informations de compilation. Mais prédire l'avenir est périlleux!

Voici une autre limitation:

Le compilateur peut générer du code qui suppose que le type à l'exécution est le type statique.

En supposant que les types statiques soient "corrects", il existe de nombreuses possibilités d'optimisation, mais ces optimisations peuvent être limitantes. Un bon exemple est celui des objets proxy, tels que la communication à distance. Supposons que vous souhaitiez avoir un objet proxy local qui transfère les appels de méthode à un objet réel dans un autre processus. Ce serait bien si le proxy était générique (afin qu'il puisse se faire passer pour n'importe quel objet) et transparent (afin que le code existant n'ait pas besoin de savoir qu'il parle à un proxy). Mais pour ce faire, le compilateur ne peut pas générer de code supposant que les types statiques sont corrects, par exemple, en appliquant statiquement des appels de méthode, car cela échouera si l’objet est réellement un proxy.

NSXPCConnection d’ ObjC ou TransparentProxy de C # (dont la mise en œuvre a nécessité quelques pessimisations au cours de l’exécution - voir la discussion ici pour une discussion).

Lorsque codegen ne dépend pas des types statiques et que vous disposez d'installations telles que le transfert de messages, vous pouvez effectuer de nombreuses tâches intéressantes avec des objets proxy, le débogage, etc.

C’est donc un exemple de ce que vous pouvez faire si vous n’êtes pas tenu de satisfaire un vérificateur de type. Les limitations ne sont pas imposées par des types statiques, mais par une vérification de type statique appliquée.

poisson ridicule
la source
2
"Un vérificateur de type statique Python 2 rejetterait le code Python 3 (et vice-versa), même s'il ne serait jamais exécuté. Mon programme protégé contre les types contient une erreur de type statique." Cela ressemble à ce dont vous avez vraiment besoin, il existe une sorte de "statique si", où le compilateur / interprète ne voit même pas le code si la condition est fausse.
David Stone
@davidstone Qui existe en c ++
Milind R
A Python 2 static type checker would reject the Python 3 code (and vice versa), even though it would never be executed. My type safe program contains a static type error. Dans n'importe quel langage statique raisonnable, vous pouvez le faire avec une IFDEFinstruction de préprocesseur de type, tout en maintenant la sécurité de type dans les deux cas.
Mason Wheeler
1
@MasonWheeler, davidstone Non, les astuces du préprocesseur et static_if sont trop statiques. Dans mon exemple, j’utilisais Python2 et Python3, mais cela aurait simplement pu être facilement AmazingModule2.0 et AmazingModule3.0, une interface changeant d’une version à l’autre. Le plus tôt que vous puissiez connaître l'interface est au moment de l'importation du module, ce qui est nécessairement au moment de l'exécution (du moins si vous souhaitez prendre en charge la liaison dynamique).
ridiculous_fish
18

Les variables de type canard sont la première chose à laquelle tout le monde pense, mais dans la plupart des cas, vous pouvez obtenir les mêmes avantages grâce à l'inférence de type statique.

Cependant, il est difficile de taper des canards dans des collections créées de manière dynamique:

>>> d = JSON.parse(foo)
>>> d['bar'][3]
12
>>> d['baz']['qux']
'quux'

Alors, quel type JSON.parserevient? Un dictionnaire de tableaux de nombres entiers-ou-dictionnaires-de-chaînes? Non, même cela n'est pas assez général.

JSON.parsedoit renvoyer une sorte de "valeur de variante" pouvant être null, bool, float, string, un tableau de l'un de ces types récursivement ou un dictionnaire de chaîne à l'un de ces types récursivement. Les principaux atouts du typage dynamique proviennent de l'existence de tels types de variantes.

Jusqu'à présent, il s'agit d'un avantage des types dynamiques , et non des langages à typage dynamique. Un langage statique décent peut parfaitement simuler un tel type. (Et même les "mauvais" langages peuvent souvent les simuler en cassant la sécurité de type sous le capot et / ou en exigeant une syntaxe d'accès maladroite.)

L'avantage des langages à typage dynamique est que de tels types ne peuvent pas être déduits par des systèmes d'inférence de type statique. Vous devez écrire le type explicitement. Mais dans de nombreux cas, y compris cette fois-ci, le code pour décrire le type est exactement aussi compliqué que le code pour analyser / construire les objets sans décrire le type, de sorte que ce n'est toujours pas un avantage.

abarnert
la source
21
Votre exemple d’analyse JSON peut être facilement géré statiquement par un type de données algébrique.
2
OK, ma réponse n'était pas assez claire. Merci. JSValue est une définition explicite d’un type dynamique, exactement ce dont je parlais. Ce sont ces types dynamiques qui sont utiles, pas les langues qui nécessitent un typage dynamique. Cependant, il est toujours important de noter que les types dynamiques ne peuvent pas être générés automatiquement par un système d'inférence de type réel, alors que la plupart des exemples les plus courants sont déductibles de manière triviale. J'espère que la nouvelle version l'explique mieux.
Abarnert
4
@ MattFenwick Algebraic Data Les types de données sont quasiment limités aux langages fonctionnels (en pratique). Qu'en est-il des langages comme Java et c #?
Spirc
4
Les ADT existent en C / C ++ en tant qu'unions étiquetées. Ce n'est pas unique aux langages fonctionnels.
Clark Gaebel
2
@spirc vous pouvez émuler des ADT dans un langage OO classique en utilisant plusieurs classes qui dérivent toutes d'une interface commune, d'appels d'exécution à getClass () ou GetType () et de contrôles d'égalité. Ou vous pouvez utiliser la double dépêche, mais je pense que cela rapporte plus en C ++. Vous pouvez donc avoir une interface JSObject et des classes JSString, JSNumber, JSHash et JSArray. Vous auriez alors besoin de code pour transformer cette structure de données "non typée" en une structure de données "typée par application". Mais vous voudrez probablement aussi faire cela dans un langage à typage dynamique.
Daniel Yankowsky
12

Tous les systèmes de types statiques pratiques à distance étant très limités par rapport au langage de programmation qui les concerne, il ne peut pas exprimer tous les invariants que le code pourrait vérifier au moment de l'exécution. Afin de ne pas contourner les garanties qu'un système de types tente de donner, il choisit donc d'être conservateur et d'interdire les cas d'utilisation permettant de passer ces contrôles, mais ne peut pas (dans le système de types) être prouvé.

Je vais faire un exemple. Supposons que vous implémentiez un modèle de données simple pour décrire des objets de données, leurs collections, etc., typés statiquement en ce sens que, si le modèle indique que l'attribut xd'objet de type Foo contient un entier, il doit toujours contenir un entier. Comme il s'agit d'une construction à l'exécution, vous ne pouvez pas la taper de manière statique. Supposons que vous stockiez les données décrites dans les fichiers YAML. Vous créez une carte de hachage (à remettre à une bibliothèque YAML ultérieurement), obtenez l' xattribut, stockez-la dans la carte, obtenez cet autre attribut qui se trouve être une chaîne, ... vous en attendez une seconde? Quel est le type de the_map[some_key]maintenant? Eh bien, nous savons que some_keyc'est le cas 'x'et le résultat doit donc être un entier, mais le système de types ne peut même pas commencer à raisonner à ce sujet.

Certains systèmes de types activement recherchés peuvent fonctionner pour cet exemple spécifique, mais ils sont extrêmement compliqués (à la fois pour les écrivains du compilateur à implémenter et pour le raisonnement du programmeur), en particulier pour quelque chose d'aussi "simple" (je veux dire, je viens de l'expliquer dans un paragraphe).

Bien sûr, la solution actuelle consiste à tout mettre en boîte, puis à lancer (ou à utiliser un tas de méthodes surchargées, dont la plupart soulèvent des exceptions "non implémentées"). Mais ceci n’est pas typé de manière statique, c’est un hack autour du système de types pour effectuer les vérifications de types au moment de l’exécution.

7043
la source
Les types génériques n'ont pas l'exigence de boxe.
Robert Harvey
@ RobertHarvey Oui. Je ne parlais pas de la boxe en Java C #, je parlais de "envelopper dans une classe de wrapper dont le seul but est de représenter une valeur de T dans un sous-type de U". Le polymorphisme paramétrique (ce que vous appelez le typage générique) ne s'applique toutefois pas à mon exemple. C'est une abstraction au moment de la compilation sur des types concrets, mais nous avons besoin d'un mécanisme de typage à l'exécution.
Il convient de souligner que le système de types de Scala est complet. Les systèmes de typage peuvent donc être moins triviaux que vous ne le croyez.
Andrea
@Andrea Je n'ai pas intentionnellement réduit ma description à turing complétude. Jamais programmé dans un tarpit de turing? Ou essayé de coder ces choses en types? À un moment donné, cela devient beaucoup trop compliqué pour être réalisable.
@delnan je suis d'accord. Je soulignais simplement que les systèmes de types peuvent faire des choses assez compliquées. J'avais l'impression que votre réponse signifiait que le système de typage ne pouvait faire qu'une vérification triviale, mais lors d'une seconde lecture, vous n'avez rien écrit de ce genre!
Andrea
7

Avec le typage dynamique, vous ne pouvez rien faire avec le typage statique, car vous pouvez implémenter le typage dynamique au-dessus d'un langage à typage statique.

Un petit exemple à Haskell:

data Data = DString String | DInt Int | DDouble Double

-- defining a '+' operator here, with explicit promotion behavior
DString a + DString b = DString (a ++ b)
DString a + DInt b = DString (a ++ show b)
DString a + DDouble b = DString (a ++ show b)
DInt a + DString b = DString (show a ++ b)
DInt a + DInt b = DInt (a + b)
DInt a + DDouble b = DDouble (fromIntegral a + b)
DDouble a + DString b = DString (show a ++ b)
DDouble a + DInt b = DDouble (a + fromIntegral b)
DDouble a + DDouble b = DDouble (a + b)

Avec suffisamment de cas, vous pouvez implémenter n'importe quel système de type dynamique.

Inversement, vous pouvez également traduire tout programme de type statique en un programme dynamique équivalent. Bien entendu, vous perdriez toutes les assurances de correction que fournit le langage de type statique au moment de la compilation.

Edit: Je voulais garder cela simple, mais voici plus de détails sur un modèle d'objet

Une fonction prend une liste de données en tant qu'arguments, effectue des calculs avec des effets secondaires dans ImplMonad et renvoie une donnée.

type Function = [Data] -> ImplMonad Data

DMember est soit une valeur membre, soit une fonction.

data DMember = DMemValue Data | DMemFunction Function

Étendez-vous Datapour inclure des objets et des fonctions. Les objets sont des listes de membres nommés.

data Data = .... | DObject [(String, DMember)] | DFunction Function

Ces types statiques sont suffisants pour implémenter tous les systèmes d'objets typés dynamiquement avec lesquels je suis familier.

NovaDenizen
la source
Ce n'est pas du tout la même chose car vous ne pouvez pas ajouter de nouveaux types sans revoir la définition de Data.
Jed
5
Vous mélangez des concepts de frappe dynamique avec une frappe faible dans votre exemple. Le typage dynamique consiste à opérer sur des types inconnus, sans définir une liste de types autorisés ni surcharger les opérations entre ceux-ci.
Hcalves
2
@Jed Une fois que vous avez implémenté le modèle objet, les types fondamentaux et les opérations primitives, aucun autre travail préparatoire n'est nécessaire. Vous pouvez facilement et automatiquement traduire des programmes dans la langue dynamique d'origine vers ce dialecte.
NovaDenizen
2
@hcalves Puisque vous parlez de surcharge dans mon code Haskell, je suppose que vous n'avez pas tout à fait la bonne idée à propos de sa sémantique. Là j'ai défini un nouvel +opérateur qui combine deux Datavaleurs dans une autre Datavaleur. Datareprésente les valeurs standard dans le système de types dynamiques.
NovaDenizen
1
@Jed: La plupart des langages dynamiques ont un petit ensemble de types "primitifs" et un moyen inductif d'introduire de nouvelles valeurs (structures de données telles que des listes). Schéma, par exemple, va assez loin avec peu plus que des atomes, des paires et des vecteurs. Vous devriez pouvoir les implémenter de la même manière que le reste du type dynamique donné.
Tikhon Jelvis
3

Membranes :

Une membrane est un wrapper autour d'un graphe d'objet entier, par opposition à un wrapper pour un seul objet. Généralement, le créateur d’une membrane commence à n’envelopper qu’un seul objet dans une membrane. L'idée clé est que toute référence d'objet qui traverse la membrane est elle-même enveloppée de manière transitoire dans la même membrane.

entrez la description de l'image ici

Chaque type est enveloppé par un type ayant la même interface, mais qui intercepte les messages et encapsule les valeurs au fur et à mesure qu'ils traversent la membrane. Quel est le type de la fonction de bouclage dans votre langage favori statiquement typé? Peut-être que Haskell a un type pour ces fonctions, mais la plupart des langages statiquement typés ne le font pas ou finissent par utiliser Object → Object, ce qui leur permet de s'affranchir de leur responsabilité en tant que vérificateurs de type.

Mike Samuel
la source
4
Oui, Haskell peut en effet utiliser des types existentiels. Si vous avez une classe de type Foo, vous pouvez créer un wrapper autour de tout type instanciant cette interface. class Foo a where ... data Wrapper = forall a. Foo a => Wrapper a
Jake McArthur
@JakeMcArthur, Merci de m'avoir expliqué. C'est encore une autre raison pour moi de m'asseoir et d'apprendre Haskell.
Mike Samuel
2
Votre membrane est une "interface" et les types d'objets sont "typés de manière existentielle" - nous savons qu'ils existent sous l'interface, mais c'est tout ce que nous savons. Les types existentiels d'abstraction de données sont connus depuis les années 80. Un bon arbitre est cs.cmu.edu/~rwh/plbook/book.pdf chapitre 21.1
Don Stewart
@DonStewart. Les classes de proxy Java sont-elles un mécanisme de type existentiel? Un endroit où les membranes deviennent difficiles est celui des langages avec des systèmes de types nominaux nommés pour les types de béton visibles en dehors de la définition de ce type. Par exemple, on ne peut pas boucler Stringpuisqu'il s'agit d'un type concret en Java. Smalltalk n'a pas ce problème car il ne tente pas de taper #doesNotUnderstand.
Mike Samuel
1

Comme quelqu'un l'a mentionné, en théorie, vous ne pouvez pas faire grand chose avec le typage dynamique, contrairement au typage statique si vous implémentiez vous-même certains mécanismes. La plupart des langues fournissent les mécanismes de relaxation de type pour prendre en charge la flexibilité de type, comme les pointeurs vides et le type d'objet racine ou une interface vide.

La meilleure question est de savoir pourquoi le typage dynamique est plus approprié et plus approprié dans certaines situations et certains problèmes.

Tout d'abord, permet de définir

Entity - J'aurais besoin d'une notion générale d'une entité dans le code. Cela peut être n'importe quoi, du nombre primitif aux données complexes.

Comportement - disons que notre entité a un certain état et un ensemble de méthodes qui permettent au monde extérieur de lui demander certaines réactions. Permet d'appeler l'état + interface de cette entité son comportement. Une même entité peut avoir plus d'un comportement combiné d'une certaine manière par les outils fournis par le langage.

Définitions des entités et de leurs comportements - chaque langage fournit des moyens d’abstractions permettant de définir les comportements (ensemble de méthodes + état interne) de certaines entités du programme. Vous pouvez attribuer un nom à ces comportements et indiquer que toutes les instances ayant ce comportement sont d'un certain type .

C'est probablement quelque chose qui n'est pas si inconnu. Et comme vous l'avez dit, vous avez compris la différence, mais quand même. Explication probablement pas complète et très précise, mais j'espère être suffisamment amusante pour apporter un peu de valeur :)

Saisie statique - le comportement de toutes les entités de votre programme est examiné au moment de la compilation, avant que le code ne soit lancé. Cela signifie que si vous voulez, par exemple, que votre entité de type Personne ait un comportement (se comporte comme) un magicien, vous devez définir une entité comme MagicianPerson et lui attribuer le comportement d'un magicien tel que throwMagic (). Si, dans votre code, vous indiquez à tort au compilateur ordinaire de Person.throwMagic (), il vous le dira."Error >>> hell, this Person has no this behavior, dunno throwing magics, no run!".

Typage dynamique - Dans les environnements de typage dynamiques, les comportements disponibles des entités ne sont vérifiés que lorsque vous essayez réellement de faire quelque chose avec certaines entités. L'exécution du code Ruby qui demande à Person.throwMagic () ne sera pas interceptée tant que votre code n'y arrivera pas vraiment. Cela semble frustrant, n'est-ce pas. Mais cela semble aussi révélateur. Sur la base de cette propriété, vous pouvez faire des choses intéressantes. Par exemple, disons que vous concevez un jeu où tout peut tourner à Magician et que vous ne savez pas vraiment à qui il appartiendra, jusqu'à ce que vous arriviez à la limite du code. Et puis la grenouille vient et vous ditesHeyYouConcreteInstanceOfFrog.include Magicet à partir de là, cette grenouille devient une grenouille particulière dotée de pouvoirs magiques. D'autres grenouilles, toujours pas. Vous voyez, dans les langages de typage statiques, vous devez définir cette relation par un moyen standard de combinaison de comportements (comme l'implémentation d'interface). Dans un langage de frappe dynamique, vous pouvez le faire en mode d'exécution et personne ne s'en souciera.

La plupart des langages de frappe dynamiques ont des mécanismes pour fournir un comportement générique qui capture tout message transmis à leur interface. Par exemple, Ruby method_missinget PHP __callsi mes souvenirs sont bons. Cela signifie que vous pouvez faire toutes sortes de choses intéressantes dans l'exécution du programme et prendre une décision de type en fonction de l'état actuel du programme. Cela apporte des outils pour la modélisation d'un problème beaucoup plus souples que dans un langage de programmation statique conservateur comme Java.

ivanjovanovic
la source