É possível ter um idioma digitado dinamicamente sem digitar o pato? [fechadas]

9

Esta pergunta foi feita aqui , mas recebeu respostas ruins e não esclareceu o problema. Acredito que justifique perguntar novamente.

Entendo que você pode digitar duck com linguagens dinamicamente ou com estaticamente (mas exemplos para isso são raros, como os modelos do C ++).

No entanto, não tenho certeza se existe algo como um idioma digitado dinamicamente sem a digitação do pato.

A digitação de pato significa que o tipo de um objeto é baseado nas operações e atributos que ele possui em um determinado momento. Existe uma maneira de digitar dinamicamente sem, inevitavelmente, oferecer suporte à digitação com patos?

Vamos dar uma olhada neste código Python para o exemplo:

def func(some_object)
    some_object.doSomething()

something = Toaster()
func(something)

Nas linguagens digitadas dinamicamente, o tipo de um objeto é conhecido apenas em tempo de execução. Portanto, quando você tenta fazer uma operação (por exemplo some_object.doSomething()), o tempo de execução tem apenas uma opção - verificar se o tipo de some_objectsuporte doSomething()é ou não , e é exatamente o que é digitação de pato.

Então, é possível ter um idioma digitado dinamicamente sem digitar o pato? Por favor explique.

Aviv Cohn
fonte
1
Eu verifiquei algumas linguagens (Javascript, Ruby e PHP) na Wikipedia e a última é supostamente dinâmica e fraca, não tipificada. Isso pode responder à sua pergunta.
Trylks
Na verdade, o tempo de execução tem a opção de verificar a assinatura de tipo do objeto antes de tentar acessar qualquer um dos seus métodos de 'campos ou chamar seus'. Isso seria "forte digitação dinâmica". Observe que o Python, por exemplo, está funcionando parcialmente dessa maneira - digamos, emite explicitamente um erro de tipo quando você tenta 1 + "1". No caso do Python, a disciplina de verificação está praticamente ausente e cabe à implementação do código do usuário verificar os tipos se o usuário (ao contrário do tempo de execução do Python) achar útil. Observe também que a tipagem do pato versus a não-pato é semelhante à tipagem estrutural e estrutural (consulte a Wikipedia).
Michael Pankov 14/10
talvez você deva usar um ganso em vez de um pato para digitar por você?
jwenting

Respostas:

19

Primeiro, para ter certeza de que estamos falando das mesmas coisas, eu começaria com algumas definições.

Digitação estática significa que erros de tipo são relatados em tempo de compilação, enquanto digitação dinâmica significa que erros de tipo são relatados em tempo de execução.

Digitar Duck significa que um pedaço de código requer que um objeto suporte as operações que são usadas e nada mais.

A digitação estrutural requer que um objeto suporte um determinado conjunto de operações (mesmo que algumas delas não possam ser usadas).

A digitação nominal requer que o objeto seja exatamente do tipo especificado ou seja um subtipo desse tipo.

Portanto, como você pode ver, a tipagem estrutural é mais rígida que a tipagem duck e a tipagem nominal é mais rígida que a estrutural.

Agora, vou falar sobre a linguagem TypeScript , porque ilustra bem a maioria dessas opções.

Considere o seguinte programa TypeScript:

interface Person {
    Name : string;
    Age : number;
}

function greet(person : Person) {
    alert("Hello, " + person.Name);
}

greet({ Name: "svick" });

Como o objeto transmitido greetnão possui a Agepropriedade, isso causa um erro de tempo de compilação, demonstrando que o TypeScript usa tipografia estrutural estática .

Apesar do erro, o código acima realmente compila o seguinte JavaScript, que funciona bem:

function greet(person) {
    alert("Hello, " + person.Name);
}

greet({ Name: "svick" });

Isso mostra que o TypeScript também usa digitação dinâmica de pato .

Se o código for compilado para algo como:

function greet(person) {
    if (!(typeof(person.Name) == 'string' && typeof(person.Age) == 'number'))
        throw 'TypeError';

    alert("Hello, " + person.Name);
}

Isso seria um exemplo de tipagem estrutural dinâmica , porque verifica se o objeto tem as propriedades necessárias dos tipos necessários, mesmo que a própria função não as exija.

Se compilado para:

function greet(person) {
    if (!(person instanceof Person))
        throw 'TypeError'

    alert("Hello, " + person.Name);
}

Isso seria um exemplo de digitação nominal dinâmica , porque verifica o nome do tipo do objeto, não sua estrutura.

O que isso tudo mostra é que a digitação dinâmica sem pato é possível (estrutural e nominal). Mas essa abordagem não é usada com muita frequência, porque combina principalmente as desvantagens da digitação sem pato (você precisa especificar os tipos explicitamente; menos flexível) e a digitação dinâmica (os erros de tipo são exibidos apenas no tempo de execução e apenas no código que realmente é executado) )

Se você quiser adicionar anotações de tipo para possibilitar a digitação sem pato, verifique os tipos em tempo de compilação.

svick
fonte
1
Eu não diria que o texto datilografado usa digitação dinâmica de pato. O fato de que, apesar dos erros de compilação, o tsc ainda produz javascript parece uma estranheza para mim, mas de qualquer maneira ... o erro de digitação foi relatado no tempo de compilação e a parte do tempo de execução é deixada para o javascript, não para texto datilografado. No entanto, o texto datilografado também usa a digitação estática do pato; quando você escreve "var t = 1;", a variável é assumida como um número.
Joel
@ Joel Sim, o TypeScript herda a digitação dinâmica de pato do JavaScript, mas isso não a torna menos uma propriedade do TypeScript. E seu exemplo demonstra inferência de tipo e não digitação de pato.
svick
Como você chamaria uma linguagem que exigisse que os métodos fossem definidos como partes de interfaces, mas permitisse que os locais de armazenamento fossem digitados usando combinações arbitrárias de interfaces? Se passar [3,4]para algum método de uma coleção mantendo [1,2]rendimento [1,2,3,4], e passar [3,4]para algum método de uma coleção [1,2]teria [4,6], os métodos deveriam ter o mesmo nome? Chamando o primeiro Sequence$Appendable$Adde o segundo, NumericVector$Addmas depois sendo capaz de dizer que uma variável deve ser interpretada como uma Sequenceou NumericVector... #
3013
... parece mais limpo do que simplesmente esperar que nomes homográficos não causem confusão. Em idiomas em que cada variável precisa ser apenas de um tipo, um tipo "puramente dinâmico" pode ser útil, mas se tipos compostos estivessem disponíveis, eu pensaria que eliminaria 99% dos casos de uso do pato "fora do azul" digitando.
Supercat 23/10
Estou inclinado a concordar com @Joel. A diferença entre um aviso e uma exceção tem pouco a ver com a digitação do idioma. TypeScript é uma biblioteca. tscé uma interface para isso. Se você usar a biblioteca, ela acionará um evento. Por padrão, se nada estiver escutando, você obtém um script. Se você ouvir e lançar uma exceção, poderá impedir que o script seja gerado. Isso muda o sistema de tipos do TypeScript? Claro que não.
Evan Carroll
3

Aparentemente (pelo que li), a tipagem de pato tem significado apenas em um contexto orientado a objetos, quando funções são anexadas como métodos a objetos. Então, quando você escreve duck.quacks(3), isso vai funcionar se o valor atual de ducktem o método quacks.

A digitação dinâmica não é necessariamente anexada a uma exibição OO com métodos.

Você pode definir o tipo realcom operador ou função associada +: real*real->reale o tipo rationalcom o operador associado +: rational*rational->rational. Então, se você escrever a+b, em um sistema de tempo verificado dinamicamente, suas variáveis ae bpoderá ter um +operador, mas você receberá um erro do tipo em tempo de execução.

A digitação dinâmica verifica a consistência categorial dos valores, possivelmente vários deles.

A digitação do Duck verifica a consistência comportamental do código com um objeto em mãos (um único, tanto quanto eu o entendo).

Em certo sentido, a tipagem de pato é uma forma de polimorfismo de tempo de execução, exceto pelo fato de que se aplica apenas aos métodos de acesso de um único objeto.

No entanto, é possível definir uma forma mais geral de polimorfismo em tempo de execução, em que o operador +a ser executado seria determinado com base em todos os argumentos. Para que um pato e uma galinha possam danse juntos se compartilharem uma função danse comum. Eles podem ter vários, para que os patos possam danse também com gansos com uma função diferente. Mas isso parece um pouco complicado. Tanto quanto me lembro, algo do tipo (provavelmente com mais estrutura) pode ter sido possível com as funções genéricas da linguagem EL1 , um precursor muito antigo das linguagens orientadas a objetos.

babou
fonte
O Python também suporta sobrecarga do operador. def add (a, b): retorna a + b é factível. Ele é convertido em um .__ add __ (b) automaticamente. ... Então sim, a digitação com patos parece exigir algum tipo de tipo definido pelo usuário avançado. Embora os membros dos dados também funcionem, as estruturas de pato também seriam possíveis, semelhante a isto: def addstuff (a, b, t): t.stuff = a.stuff + b.stuff
StarWeaver
@ StarWeaver Bem, sim. Mas o que me incomoda com o seu exemplo é a assimetria entre os operandos. Se houver um erro de tipo, é porque a não possui o método adequado ou porque existe uma incompatibilidade de tipo entre os métodos de a e o tipo de b. Portanto, dependendo da situação, você recebe uma exceção diferente (se minha memória do Python estiver correta). Eu poderia querer um nível de abstração em que apenas obtivesse uma exceção dizendo que aeb não podem + se unir (desculpe a frase estranha). Mas isso pode ser uma questão de gosto. Eu também gostaria de afirmar que + é comutativo.
babou
-2

Uma resposta por analogia:

Você pode comprar um conversível e nunca largar a capota? Certo. Provavelmente não é a melhor maneira de gastar seus recursos, já que você é pago a mais por alguns recursos (por exemplo, capota conversível, reforço estrutural extra devido à falta de teto como elemento estrutural) e obteve alguns resultados piores (por exemplo, ruído extra na estrada, possivelmente menor) segurança contra colisões, compartimentos de armazenamento menores) como resultado do investimento nesse recurso que você não usará. Mas é tecnicamente viável.

É o mesmo com idiomas dinâmicos e digitação de pato. Você desistiu da maior eficiência e das garantias de segurança do tipo tempo de compilação das linguagens estáticas. Para quê? Geralmente pela simplicidade da digitação de patos. Variáveis ​​e coleções podem conter qualquer coisa, e você não precisa especificar muito o que é necessário. Coleções mistas como [ 12, "gumbo", 12.4, 4+4j ](um número inteiro, uma sequência de caracteres, um valor de ponto flutuante e um valor complexo) são triviais e você não tem a conversão de tipo constante que vê no código Java (por exemplo).

É possível em uma linguagem dinâmica como o Python criar objetos que não são do tipo pato:

class Hungarian(object):
    self __init__(self):
        self.value = []
    self addInt(self, intValue):
        self.value.append(intValue)
    self addFloat(self, floatValue):
        self.value.append(floatValue)
    self addComplex(self, complexValue):
        self.value.append(complexValue)
    # ...

Mas, como você pode notar, não há uma verificação real dos tipos, e cada um dos métodos é implementado com uma estrutura interna do tipo pato ( list). Depois de pagar o preço pelo dinamismo, você também pode colocar o topo para baixo e obter a simplicidade resultante:

class NotHungarian(object):
    def __init__(self):
        self.value = []
    def add(self, whatever):
        self.value.append(whatever)
Jonathan Eunice
fonte
De que maneira essa Hungarianclasse não é digitada? Como você apontou, não há verificação de tipos.
svick 14/10
@svick Destina-se a ser usado de uma maneira específica de tipo, como Java, com métodos separados, dependendo não da ação executada, mas também dos tipos utilizados. A digitação de pato usaria o mesmo método independente do tipo que está sendo adicionado, assim como NotHungarianfaz. A digitação com pato depende não apenas da verificação do tipo, mas também do uso do mesmo nome de chamada / método / mensagem ( add). ( NotHungarianTambém usa um nome de método, addque de comum com outros objetos Python, como setIt "grasna" como os outros objetos / classes..)
Jonathan Eunice
1
Esta resposta está um pouco desinformada sobre a motivação para “desistir da maior eficiência e das garantias de segurança do tipo tempo de compilação das linguagens estáticas”. A motivação era abandonar os grilhões de um sistema do tipo Fortran ou C, porque esses sistemas frequentemente atrapalham (pense nas seqüências de comprimento fixo de Pascal), impedem a programação genérica e tornam recursos incrivelmente poderosos, como evalinviáveis. A motivação nunca foi se livrar dos tipos, e algumas linguagens dinâmicas têm "digitação gradual". O MJD tem uma boa apresentação sobre tipagem estática versus dinâmica.
amon
@amon Discordo. As linguagens tipicamente estáticas geralmente têm uma vantagem bastante significativa sobre as linguagens dinamicamente, porque usaram tipos não-caixa / de valor, estruturas em vez de dictos, etc. Essa não é a única razão para escolher um idioma. Uso dinâmico sempre que possível, e a penalidade de desempenho é limitada ou sem importância na maioria das vezes. E sim, existem outras motivações para a escolha dinâmica. Mas você desiste de coisas reais para ir para lá.
Jonathan Eunice