Que funcionalidade a digitação dinâmica permite? [fechadas]

91

Estou usando python há alguns dias e acho que entendo a diferença entre digitação dinâmica e estática. O que não entendo é em que circunstâncias seria preferido. É flexível e legível, mas à custa de mais verificações em tempo de execução e testes de unidade adicionais necessários.

Além de critérios não funcionais, como flexibilidade e legibilidade, que motivos existem para escolher a digitação dinâmica? O que posso fazer com a digitação dinâmica que não é possível de outra maneira? Que exemplo de código específico você pode imaginar que ilustra uma vantagem concreta da digitação dinâmica?

Justin984
fonte
5
Teoricamente, também não há nada que você não possa fazer, desde que os idiomas estejam completos em Turing . A pergunta mais interessante para mim é o que é fácil ou natural em um versus o outro. Há coisas que faço regularmente em Python que nem considero em C ++, mesmo sabendo que é capaz.
Mark Ransom
28
Como Chris Smith escreve em seu excelente ensaio O que saber antes de debater sistemas de tipos : "O problema, nesse caso, é que a maioria dos programadores tem experiência limitada e não experimentou muitas linguagens. Para o contexto, aqui, seis ou sete não conta como "muito". ... Duas conseqüências interessantes disso são: (1) Muitos programadores usaram linguagens de tipos estatísticos muito ruins. (2) Muitos programadores usaram linguagens de tipos dinâmicos muito mal. "
Daniel Pryden
3
@suslik: Se as primitivas de linguagem têm tipos sem sentido, é claro que você pode fazer coisas sem sentido com os tipos. Isso não tem nada a ver com a diferença entre digitação estática e dinâmica.
quer
10
@CzarekTomczak: Sim, esse é um recurso de algumas linguagens dinamicamente tipadas. Mas é possível que uma linguagem digitada estaticamente seja modificável em tempo de execução. Por exemplo, o Visual Studio permite reescrever o código C # enquanto você estiver em um ponto de interrupção no depurador e até rebobinar o ponteiro de instruções para executar novamente o código com novas alterações. Como citei Chris Smith em meu outro comentário: "Muitos programadores usaram linguagens estaticamente muito ruins" - não julgue todas as linguagens estaticamente conhecidas.
Daniel Pryden
11
@ WarrenP: Você afirma que "os sistemas dinâmicos reduzem a quantidade de material extra que eu tenho que digitar" - mas então você compara Python a C ++. Essa não é uma comparação justa: é claro que o C ++ é mais detalhado que o Python, mas não é por causa da diferença em seus sistemas de tipos, é por causa da diferença em suas gramáticas. Se você quiser apenas reduzir o número de caracteres na fonte do programa, aprenda J ou APL: garanto que eles serão mais curtos. Uma comparação mais justa seria comparar Python a Haskell. (Para o registro: Eu amo Python e preferem sobre C ++, mas eu gosto Haskell ainda mais.)
Daniel Pryden

Respostas:

50

Como você pediu um exemplo específico, eu darei um.

O ORM maciço de Rob Conery tem 400 linhas de código. É tão pequeno porque Rob é capaz de mapear tabelas SQL e fornecer resultados de objetos sem exigir muitos tipos estáticos para espelhar as tabelas SQL. Isso é feito usando o dynamictipo de dados em C #. A página da Web de Rob descreve esse processo em detalhes, mas parece claro que, nesse caso de uso específico, a digitação dinâmica é em grande parte responsável pela brevidade do código.

Compare com o Dapper de Sam Saffron , que usa tipos estáticos; a SQLMapperclasse sozinha possui 3000 linhas de código.

Observe que as isenções de responsabilidade usuais se aplicam e sua milhagem pode variar; Dapper tem objetivos diferentes dos de Massive. Apenas aponto isso como um exemplo de algo que você pode fazer em 400 linhas de código que provavelmente não seriam possíveis sem a digitação dinâmica.


A digitação dinâmica permite adiar suas decisões de digitação para o tempo de execução. Isso é tudo.

Se você usa uma linguagem de tipo dinâmico ou estaticamente, suas escolhas de tipo ainda devem ser sensatas. Você não adicionará duas cadeias e esperará uma resposta numérica, a menos que as cadeias contenham dados numéricos; caso contrário, obterá resultados inesperados. Um idioma digitado estaticamente não permitirá que você faça isso em primeiro lugar.

Os defensores das linguagens de tipo estaticamente apontam que o compilador pode fazer uma quantidade substancial de "verificação de integridade" do seu código em tempo de compilação, antes que uma única linha seja executada. Esta é uma coisa boa ™.

C # tem a dynamicpalavra - chave, que permite adiar a decisão de digitar em tempo de execução sem perder os benefícios da segurança de tipo estático no restante do seu código. A inferência de tipo ( var) elimina grande parte da dor de escrever em um idioma estaticamente digitado, removendo a necessidade de sempre declarar explicitamente os tipos.


Linguagens dinâmicas parecem favorecer uma abordagem mais interativa e imediata à programação. Ninguém espera que você precise escrever uma classe e passar por um ciclo de compilação para digitar um pouco do código Lisp e assisti-lo executar. No entanto, é exatamente isso que eu devo fazer em c #.

Robert Harvey
fonte
22
Se eu adicionasse duas seqüências numéricas, ainda não esperaria um resultado numérico.
Pd3
22
@ Robert Concordo com a maior parte da sua resposta. No entanto, observe que existem linguagens de tipo estático com loops interativos de leitura e avaliação, como Scala e Haskell. Pode ser que o C # simplesmente não seja uma linguagem particularmente interativa.
Andres F.
14
Preciso aprender um Haskell.
Robert Harvey
7
@RobertHarvey: Você pode se surpreender / impressionar com o F # se ainda não o experimentou. Você obtém toda a segurança de tipo (em tempo de compilação) que normalmente obtém em uma linguagem .NET, exceto que raramente é necessário declarar qualquer tipo. A inferência de tipo em F # vai além do que está disponível / funciona em C #. Também: semelhante ao que Andrés e Daniel estão apontando para fora, F # interativo é parte do Visual Studio ...
Steven Evers
8
"Você não adicionará duas strings juntas e espera uma resposta numérica, a menos que as strings contenham dados numéricos; caso contrário, você obterá resultados inesperados" desculpe, isso não tem nada a ver com digitação dinâmica versus estática , isso é forte versus digitação fraca .
Vartec 5/10
26

Frases como "digitação estática" e "digitação dinâmica" são muito usadas, e as pessoas tendem a usar definições sutilmente diferentes, então vamos começar esclarecendo o que queremos dizer.

Considere uma linguagem que tenha tipos estáticos que são verificados em tempo de compilação. Mas digamos que um erro de tipo gere apenas um aviso não fatal e, em tempo de execução, tudo é tipificado por pato. Esses tipos estáticos são apenas para conveniência do programador e não afetam o codegen. Isso ilustra que a digitação estática por si só não impõe limitações e não é mutuamente exclusiva na digitação dinâmica. (Objective-C é muito parecido com isso.)

Mas a maioria dos sistemas do tipo estático não se comporta dessa maneira. Há duas propriedades comuns de sistemas de tipo estático que podem impor limitações:

O compilador pode rejeitar um programa que contém um erro de tipo estático.

Essa é uma limitação, porque muitos programas seguros de tipo necessariamente contêm um erro de tipo estático.

Por exemplo, eu tenho um script Python que precisa ser executado como Python 2 e Python 3. Algumas funções alteraram seus tipos de parâmetros entre Python 2 e 3, portanto, tenho um código como este:

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

Um verificador de tipo estático do Python 2 rejeitaria o código do Python 3 (e vice-versa), mesmo que nunca fosse executado. Meu programa de segurança de tipo contém um erro de tipo estático.

Como outro exemplo, considere um programa Mac que deseja executar no OS X 10.6, mas aproveite os novos recursos do 10.7. Os métodos 10.7 podem ou não existir em tempo de execução, e cabe a mim, o programador, detectá-los. Um verificador de tipo estático é forçado a rejeitar meu programa para garantir a segurança do tipo ou aceitar o programa, além da possibilidade de produzir um erro de tipo (falta de função) em tempo de execução.

A verificação de tipo estático pressupõe que o ambiente de tempo de execução seja descrito adequadamente pelas informações de tempo de compilação. Mas prever o futuro é arriscado!

Aqui está mais uma limitação:

O compilador pode gerar código que assume que o tipo de tempo de execução é o tipo estático.

Assumir que os tipos estáticos estão "corretos" oferece muitas oportunidades de otimização, mas essas otimizações podem ser limitativas. Um bom exemplo são objetos proxy, por exemplo, remoting. Digamos que você deseje ter um objeto proxy local que encaminhe invocações de métodos para um objeto real em outro processo. Seria bom se o proxy fosse genérico (para que ele possa se disfarçar como qualquer objeto) e transparente (para que o código existente não precise saber que está falando com um proxy). Mas, para fazer isso, o compilador não pode gerar código que pressupõe que os tipos estáticos estejam corretos, por exemplo, através de chamadas de método com estaticamente embutido, porque isso falhará se o objeto for realmente um proxy.

Exemplos dessa comunicação remota em ação incluem o NSXPCConnection da ObjC ou o TransparentProxy do C # (cuja implementação exigiu algumas pessimizações no tempo de execução - veja aqui para uma discussão).

Quando o codegen não depende dos tipos estáticos e você possui recursos como encaminhamento de mensagens, pode fazer muitas coisas legais com objetos proxy, depuração etc.

Portanto, é uma amostra de algumas das coisas que você pode fazer se não precisar satisfazer um verificador de tipos. As limitações não são impostas por tipos estáticos, mas pela verificação de tipo estático imposta.

ridiculous_fish
fonte
2
"Um verificador de tipo estático do Python 2 rejeitaria o código Python 3 (e vice-versa), mesmo que nunca fosse executado. Meu programa de segurança de tipo contém um erro de tipo estático." Parece que você realmente precisa de algum tipo de "estático se", em que o compilador / intérprete nem vê o código se a condição for falsa.
David Stone
@davidstone Isso existe em 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. Em qualquer linguagem estática razoável, você pode fazer isso com uma IFDEFinstrução de pré-processador de tipo, mantendo a segurança de tipo nos dois casos.
Mason Wheeler
1
@MasonWheeler, davidstone Não, truques de pré-processador e static_if são muito estáticos. No meu exemplo, usei o Python2 e o Python3, mas ele poderia facilmente ter sido o AmazingModule2.0 e o AmazingModule3.0, onde algumas interfaces foram alteradas entre as versões. O mais cedo que você pode conhecer a interface está no tempo de importação do módulo, que é necessariamente em tempo de execução (pelo menos se você deseja oferecer suporte à vinculação dinâmica).
Ridiculous_fish
18

Variáveis ​​do tipo pato são a primeira coisa que todos pensam, mas na maioria dos casos, você pode obter os mesmos benefícios através da inferência estática de tipo.

Mas digitar pato em coleções criadas dinamicamente é difícil de se obter de qualquer outra maneira:

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

Então, que tipo JSON.parseretorna? Um dicionário de matrizes-de-inteiros-ou-dicionários-de-strings? Não, mesmo isso não é geral o suficiente.

JSON.parseprecisa retornar algum tipo de "valor variante" que possa ser nulo, bool, float, string, matriz de qualquer um desses tipos recursivamente ou dicionário da string para qualquer um desses tipos recursivamente. Os principais pontos fortes da digitação dinâmica são os tipos de variantes.

Até agora, esse é um benefício de tipos dinâmicos , não de linguagens dinamicamente tipadas. Uma linguagem estática decente pode simular perfeitamente qualquer tipo desse tipo. (E mesmo idiomas "ruins" geralmente podem simulá-los, quebrando a segurança do tipo sob o capô e / ou exigindo a sintaxe de acesso desajeitada.)

A vantagem de linguagens de tipo dinâmico é que esses tipos não podem ser inferidos por sistemas de inferência de tipo estático. Você precisa escrever o tipo explicitamente. Mas, em muitos casos, incluindo o código uma vez, o código para descrever o tipo é exatamente tão complicado quanto o código para analisar / construir os objetos sem descrever o tipo, de modo que ainda não é necessariamente uma vantagem.

abarnert
fonte
21
Seu exemplo de análise JSON pode ser facilmente manipulado estaticamente por um Tipo de Dados Algébrico.
2
OK, minha resposta não foi clara o suficiente; obrigado. Esse JSValue é uma definição explícita de um tipo dinâmico, exatamente do que eu estava falando. São esses tipos dinâmicos que são úteis, não os idiomas que exigem digitação dinâmica. No entanto, ainda é relevante que tipos dinâmicos não possam ser gerados automaticamente por qualquer sistema real de inferência de tipos, enquanto a maioria dos exemplos comuns de pessoas é trivialmente inferível. Espero que a nova versão explique melhor.
abarnert
4
Os tipos de dados algébricos do @MattFenwick são praticamente restritos a linguagens funcionais (na prática). E linguagens como Java e c #?
spirc
4
Os ADTs existem em C / C ++ como uniões marcadas. Isso não é exclusivo das linguagens funcionais.
Clark Gaebel
2
@spirc, você pode emular ADTs em uma linguagem OO clássica usando várias classes derivadas de uma interface comum, chamadas em tempo de execução para getClass () ou GetType () e verificações de igualdade. Ou você pode usar o envio duplo, mas acho que compensa mais em C ++. Portanto, você pode ter uma interface JSObject e as classes JSString, JSNumber, JSHash e JSArray. Você precisaria de algum código para transformar essa estrutura de dados "não digitada" em uma estrutura de dados "digitada pelo aplicativo". Mas você provavelmente também desejaria fazer isso em uma linguagem de tipo dinâmico.
Daniel Yankowsky 9/10/12
12

Como todo sistema de tipo estático remotamente prático é severamente limitado em comparação com a linguagem de programação em questão, ele não pode expressar todos os invariantes que código poderia verificar em tempo de execução. Para não contornar as garantias que um sistema de tipos tenta dar, ele opta por ser conservador e não permitir casos de uso que passariam nessas verificações, mas não pode ser comprovado (no sistema de tipos).

Eu vou fazer um exemplo. Suponha que você implemente um modelo de dados simples para descrever objetos de dados, coleções deles etc., que são estaticamente digitados no sentido de que, se o modelo diz que o atributo xdo objeto do tipo Foo possui um número inteiro, ele deve sempre conter um número inteiro. Como essa é uma construção de tempo de execução, você não pode digitá-la estaticamente. Suponha que você armazene os dados descritos nos arquivos YAML. Você cria um mapa de hash (para ser entregue a uma biblioteca YAML posteriormente), obtém o xatributo, armazena-o no mapa, obtém o outro atributo que, por acaso, é uma string, ... espera um segundo? Qual é o tipo de the_map[some_key]agora? Bem, tiro, sabemos que some_keyé 'x'e, portanto, o resultado deve ser um número inteiro, mas o sistema de tipos não pode sequer começar a raciocinar sobre isso.

Alguns sistemas de tipos pesquisados ​​ativamente podem funcionar para esse exemplo específico, mas eles são extremamente complicados (tanto para escritores de compiladores implementarem quanto para que o programador raciocine), especialmente para algo tão "simples" (quero dizer, eu apenas expliquei isso em um parágrafo).

Obviamente, a solução de hoje é encaixotar tudo e depois lançar (ou ter vários métodos substituídos, a maioria dos quais gera exceções "não implementadas"). Mas isso não é estaticamente digitado, é um hack no sistema de tipos para fazer as verificações em tempo de execução.

user7043
fonte
Tipos genéricos não têm o requisito de boxe.
Robert Harvey
@RobertHarvey Sim. Eu não estava falando com o boxe em Java C #, mas sobre "encerrá-lo em alguma classe de invólucro cujo único objetivo é representar um valor de T em um subtipo de U". O polimorfismo paramétrico (o que você chama de digitação genérica), no entanto, não se aplica ao meu exemplo. É uma abstração em tempo de compilação sobre tipos concretos, mas precisamos de um mecanismo de digitação em tempo de execução.
Vale a pena ressaltar que o sistema de tipos de Scala é Turing completo. Portanto, os sistemas de tipos podem ser menos triviais do que você imagina.
Andrea
@ Andrea, intencionalmente, não reduzi minha descrição à perfeição. Já programado em um tarpit turing? Ou tentou codificar essas coisas em tipos? Em algum momento, torna-se muito complicado para ser viável.
@delnan Eu concordo. Eu estava apenas apontando que os sistemas de tipos podem fazer coisas bastante complicadas. Tive a impressão de que sua resposta significava que o sistema de tipos só pode fazer verificações triviais, mas em uma segunda leitura você não escreveu nada parecido com isso!
Andrea
7

Não há nada que você possa fazer com a digitação dinâmica que você não possa fazer com a digitação estática, porque você pode implementar a digitação dinâmica em cima de um idioma digitado estaticamente.

Um pequeno exemplo em 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)

Com casos suficientes, você pode implementar qualquer sistema de tipo dinâmico.

Por outro lado, você também pode converter qualquer programa estaticamente digitado em um dinâmico equivalente. Obviamente, você perderia todas as garantias de correção em tempo de compilação fornecidas pela linguagem de tipo estaticamente.

Edit: Eu queria manter isso simples, mas aqui estão mais detalhes sobre um modelo de objeto

Uma função pega uma lista de dados como argumentos e executa cálculos com efeitos colaterais no ImplMonad e retorna um dado.

type Function = [Data] -> ImplMonad Data

DMember é um valor de membro ou uma função.

data DMember = DMemValue Data | DMemFunction Function

Estenda Datapara incluir objetos e funções. Objetos são listas de membros nomeados.

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

Esses tipos estáticos são suficientes para implementar todos os sistemas de objetos com tipos dinâmicos com os quais estou familiarizado.

NovaDenizen
fonte
Isso não é o mesmo, porque você não pode adicionar novos tipos sem revisitar a definição de Data.
Jed
5
Você está misturando conceitos de digitação dinâmica com digitação fraca no seu exemplo. A digitação dinâmica é sobre operar em tipos desconhecidos, não definir uma lista de tipos permitidos e sobrecarregar operações entre eles.
Hcalves #
2
@ Jed Depois de implementar o modelo de objeto, tipos fundamentais e operações primitivas, nenhuma outra base é necessária. Você pode converter programas de maneira fácil e automática no idioma dinâmico original para esse dialeto.
NovaDenizen
2
@ hcalves Como você está se referindo à sobrecarga no meu código Haskell, suspeito que você não tenha a idéia certa sobre a semântica. Lá eu defini um novo +operador que combina dois Datavalores em outro Datavalor. Datarepresenta os valores padrão no sistema de tipos dinâmicos.
NovaDenizen
1
@ Jed: A maioria das linguagens dinâmicas possui um pequeno conjunto de tipos "primitivos" e alguma maneira indutiva de introduzir novos valores (estruturas de dados como listas). O esquema, por exemplo, chega muito longe com pouco mais que átomos, pares e vetores. Você deve implementá-los da mesma maneira que o restante do tipo dinâmico especificado.
Tikhon Jelvis 10/10
3

Membranas :

Uma membrana é um invólucro em torno de um gráfico de objeto inteiro, em oposição a um invólucro para apenas um único objeto. Normalmente, o criador de uma membrana começa a envolver apenas um único objeto em uma membrana. A idéia principal é que qualquer referência a objeto que atravesse a membrana seja ela própria transitória envolvida na mesma membrana.

insira a descrição da imagem aqui

Cada tipo é envolvido por um tipo que tem a mesma interface, mas que intercepta mensagens, quebra e quebra valores quando cruzam a membrana. Qual é o tipo da função de quebra automática em seu idioma estaticamente digitado favorito? Talvez Haskell tenha um tipo para essas funções, mas a maioria das linguagens com estaticamente não usa ou acabam usando Object → Object, abdicando efetivamente de sua responsabilidade como verificadores de tipos.

Mike Samuel
fonte
4
Sim, Haskell pode de fato fazer isso usando tipos existenciais. Se você tiver alguma classe de tipo Foo, poderá criar um invólucro em torno de qualquer tipo que instanciar essa interface. class Foo a where ... data Wrapper = forall a. Foo a => Wrapper a
Jake McArthur
@JakeMcArthur, obrigado por explicar. Essa é mais uma razão para eu me sentar e aprender Haskell.
Mike Samuel
2
Sua membrana é uma 'interface' e os tipos de objetos são "existencialmente digitados" - isto é, sabemos que eles existem sob a interface, mas é tudo o que sabemos. Tipos existentes para abstração de dados são conhecidos desde os anos 80. Uma boa referência é cs.cmu.edu/~rwh/plbook/book.pdf capítulo 21.1
Don Stewart
@DonStewart. As classes de proxy Java são um mecanismo de tipo existencial? Um lugar em que as membranas se tornam difíceis é em linguagens com sistemas de tipos nominais que possuem nomes para tipos concretos visíveis fora da definição desse tipo. Por exemplo, não é possível quebrar, Stringpois é um tipo concreto em Java. Smalltalk não tem esse problema porque não tenta digitar #doesNotUnderstand.
9788 Mike #
1

Como alguém mencionado, em teoria, não há muito que você possa fazer com a digitação dinâmica que você não poderia fazer com a digitação estática se implementasse determinados mecanismos por conta própria. A maioria dos idiomas fornece os mecanismos de relaxamento de tipo para oferecer suporte à flexibilidade, como ponteiros nulos e tipo de objeto raiz ou interface vazia.

A melhor pergunta é por que a digitação dinâmica é mais adequada e mais apropriada em determinadas situações e problemas.

Primeiro, vamos definir

Entidade - eu precisaria de uma noção geral de alguma entidade no código. Pode ser qualquer coisa, desde número primitivo a dados complexos.

Comportamento - digamos que nossa entidade tenha algum estado e um conjunto de métodos que permitem ao mundo exterior instruir a entidade a certas reações. Vamos chamar o estado + interface desta entidade de seu comportamento. Uma entidade pode ter mais de um comportamento combinado de uma certa maneira pelas ferramentas fornecidas pela linguagem.

Definições de entidades e seus comportamentos - toda linguagem fornece alguns meios de abstração que ajudam a definir comportamentos (conjunto de métodos + estado interno) de determinadas entidades no programa. Você pode atribuir um nome a esses comportamentos e dizer que todas as instâncias que possuem esse comportamento são de determinado tipo .

Provavelmente isso é algo que não é tão familiar. E como você disse, entendeu a diferença, mas ainda assim. Provavelmente não é uma explicação completa e precisa, mas espero que seja divertida o suficiente para trazer algum valor :)

Tipagem estática - o comportamento de todas as entidades do seu programa é examinado em tempo de compilação, antes do código ser iniciado. Isso significa que se você deseja, por exemplo, que sua entidade do tipo Person tenha um comportamento (para se comportar como) Mágico, será necessário definir a entidade MagicianPerson e fornecer comportamentos de um mago como throwMagic (). Se você no seu código, por engano, informe para o compilador Person.throwMagic () comum, informará"Error >>> hell, this Person has no this behavior, dunno throwing magics, no run!".

Digitação dinâmica - em ambientes de digitação dinâmica, os comportamentos disponíveis das entidades não são verificados até que você realmente tente fazer algo com determinada entidade. A execução de código Ruby que solicita a Person.throwMagic () não será capturada até que seu código realmente chegue lá. Isso parece frustrante, não é? Mas parece revelador também. Com base nessa propriedade, você pode fazer coisas interessantes. Por exemplo, digamos que você crie um jogo em que qualquer coisa possa se voltar para o Magician e você realmente não sabe quem será, até chegar ao ponto certo do código. E então Sapo vem e você dizHeyYouConcreteInstanceOfFrog.include Magice a partir de então este sapo se torna um sapo em particular que possui poderes mágicos. Outros sapos, ainda não. Veja bem, nas linguagens estáticas de digitação, você teria que definir essa relação por alguma média padrão de combinação de comportamentos (como implementação de interface). Na linguagem de digitação dinâmica, você pode fazer isso em tempo de execução e ninguém se importará.

A maioria das linguagens de digitação dinâmica possui mecanismos para fornecer um comportamento genérico que captura qualquer mensagem passada para sua interface. Por exemplo, Ruby method_missinge PHP, __callse bem me lembro. Isso significa que você pode fazer qualquer tipo de coisa interessante no tempo de execução do programa e tomar uma decisão de tipo com base no estado atual do programa. Isso traz ferramentas para modelagem de um problema que são muito mais flexíveis do que em, digamos, linguagem de programação estática conservadora como Java.

ivanjovanovic
fonte