Acho que entendo digitação forte , mas toda vez que procuro exemplos para a digitação fraca, acabo encontrando exemplos de linguagens de programação que simplesmente coagem / convertem tipos automaticamente.
Por exemplo, neste artigo chamado Digitação: Forte vs. Fraco, Estático vs. Dinâmico diz que o Python é fortemente digitado porque você recebe uma exceção se tentar:
Pitão
1 + "1"
Traceback (most recent call last):
File "", line 1, in ?
TypeError: unsupported operand type(s) for +: 'int' and 'str'
No entanto, isso é possível em Java e em C #, e não os consideramos fracamente digitados apenas para isso.
Java
int a = 10;
String b = "b";
String result = a + b;
System.out.println(result);
C #
int a = 10;
string b = "b";
string c = a + b;
Console.WriteLine(c);
Neste outro artigo chamado Weakly Type Languages, o autor diz que Perl é digitado de maneira fraca, simplesmente porque eu posso concatenar uma string para um número e vice-versa sem nenhuma conversão explícita.
Perl
$a=10;
$b="a";
$c=$a.$b;
print $c; #10a
Portanto, o mesmo exemplo faz com que Perl seja digitado fracamente, mas não Java e C # ?.
Nossa, isso é confuso
Os autores parecem sugerir que uma linguagem que impede a aplicação de certas operações em valores de tipos diferentes é fortemente tipada e, ao contrário, significa tipicamente fracamente.
Portanto, em algum momento, senti-me solicitado a acreditar que se um idioma fornece muitas conversões automáticas ou coerção entre tipos (como perl) pode acabar sendo considerado com uma tipagem fraca, enquanto outros idiomas que fornecem apenas algumas conversões podem acabar sendo considerado fortemente tipado.
Estou inclinado a acreditar, no entanto, que devo estar errado nessa interpretação, simplesmente não sei por que nem como explicá-la.
Então, minhas perguntas são:
- O que realmente significa para um idioma ser verdadeiramente fracamente digitado?
- Você poderia mencionar bons exemplos de digitação fraca que não estão relacionados à conversão automática / coerção automática feita pelo idioma?
- Um idioma pode ser fracamente digitado e fortemente digitado ao mesmo tempo?
Respostas:
ATUALIZAÇÃO: Esta questão foi o assunto do meu blog em 15 de outubro de 2012. Obrigado pela ótima pergunta!
Significa "esta linguagem usa um sistema de tipos que considero desagradável". Uma linguagem "fortemente tipada", ao contrário, é uma linguagem com um sistema de tipos que eu acho agradável.
Os termos são essencialmente sem sentido e você deve evitá-los. A Wikipedia lista onze significados diferentes para "fortemente tipado", vários dos quais são contraditórios. Isso indica que as chances de confusão que estão sendo criadas são altas em qualquer conversa envolvendo o termo "tipicamente fortemente" ou "tipicamente fraco".
Tudo o que você realmente pode dizer com certeza é que uma linguagem "fortemente tipada" em discussão tem alguma restrição adicional no sistema de tipos, em tempo de execução ou compilação, que falta uma linguagem "tipicamente fraca" em discussão. O que essa restrição pode ser não pode ser determinada sem outro contexto.
Em vez de usar "fortemente digitado" e "pouco digitado", você deve descrever em detalhes que tipo de segurança de tipo você quer dizer. Por exemplo, C # é uma linguagem de tipo estaticamente e uma linguagem segura de tipo e uma linguagem segura de memória , na maior parte. O C # permite que todas as três formas de digitação "forte" sejam violadas. O operador de conversão viola a digitação estática; ele diz ao compilador "Eu sei mais sobre o tipo de tempo de execução dessa expressão do que você". Se o desenvolvedor estiver errado, o tempo de execução lançará uma exceção para proteger a segurança do tipo. Se o desenvolvedor desejar quebrar a segurança de tipo ou a segurança de memória, poderá fazê-lo desligando o sistema de segurança de tipo criando um bloco "inseguro". Em um bloco não seguro, você pode usar a magia do ponteiro para tratar um int como um flutuador (violando a segurança do tipo) ou para gravar na memória que você não possui. (Violando a segurança da memória.)
O C # impõe restrições de tipo que são verificadas no tempo de compilação e no tempo de execução, tornando-o um idioma "fortemente tipado" comparado aos idiomas que fazem menos verificação no tempo de compilação ou menos verificação no tempo de execução. O C # também permite que você, em circunstâncias especiais, execute uma execução final com essas restrições, tornando-o um idioma "de tipo fraco" em comparação com os idiomas que não permitem a execução final.
Qual é mesmo? É impossível dizer; depende do ponto de vista do falante e de sua atitude em relação aos vários recursos do idioma.
fonte
Como outros observaram, os termos "fortemente digitado" e "fracamente digitado" têm tantos significados diferentes que não há uma resposta única para sua pergunta. No entanto, como você mencionou especificamente o Perl na sua pergunta, deixe-me tentar explicar em que sentido o Perl é fracamente digitado.
O ponto é que, em Perl, não existe uma "variável inteira", uma "variável flutuante", uma "variável de string" ou uma "variável booleana". De fato, na medida em que o usuário pode (normalmente) dizer, não há ainda inteiro, float, string ou boolean valores : tudo que você tem são "escalares", que são todas essas coisas ao mesmo tempo. Então você pode, por exemplo, escrever:
Obviamente, como você observou corretamente, tudo isso pode ser visto apenas como coerção de tipo. Mas o ponto é que, em Perl, os tipos são sempre coagidos. De fato, é muito difícil para um usuário dizer qual pode ser o "tipo" interno de uma variável: na linha 2 do meu exemplo acima, perguntando se o valor de
$bar
é a sequência"9"
ou o número9
é praticamente sem sentido, pois, como No que diz respeito a Perl, essas são a mesma coisa . Na verdade, é ainda possível para um escalar Perl ter internamente tanto uma string e um valor numérico ao mesmo tempo, como é por exemplo o caso para$foo
depois linha 2 acima.O outro lado disso tudo é que, como as variáveis Perl não são tipadas (ou melhor, não expõem seu tipo interno ao usuário), os operadores não podem ser sobrecarregados para fazer coisas diferentes para diferentes tipos de argumentos; você não pode simplesmente dizer "esse operador fará X para números e Y para seqüências de caracteres", porque o operador não pode (não vai) dizer que tipo de valores são seus argumentos.
Assim, por exemplo, o Perl possui e precisa de um operador de adição numérica (
+
) e um operador de concatenação de strings (.
): como você viu acima, é perfeitamente bom adicionar strings ("1" + "2" == "3"
) ou concatenar números (1 . 2 == 12
). Da mesma forma, os operadores de comparação numérica==
,!=
,<
,>
,<=
,>=
e<=>
comparar os valores numéricos de seus argumentos, enquanto os operadores de comparação de stringeq
,ne
,lt
,gt
,le
,ge
ecmp
compará-los lexicographically como strings. Então2 < 10
, mas2 gt 10
(mas"02" lt 10
enquanto"02" == 2
). (Lembre-se de que outras línguas, como o JavaScript, tentam acomodar a digitação fraca do tipo Perl enquantotambém sobrecarregando o operador. Isso geralmente leva à feiúra, como a perda de associatividade por+
.)(O ponto principal aqui é que, por razões históricas, o Perl 5 tem alguns casos de canto, como os operadores lógicos bit a bit, cujo comportamento depende da representação interna de seus argumentos. Esses são geralmente considerados uma falha de design irritante, pois a representação interna pode mudar por razões surpreendentes e, portanto, prever o que esses operadores fazem em uma determinada situação pode ser complicado.)
Tudo o que disse, pode-se argumentar que Perl faz ter tipos fortes; eles não são apenas o tipo de tipo que você poderia esperar. Especificamente, além do tipo "escalar" discutido acima, o Perl também possui dois tipos estruturados: "matriz" e "hash". Essas são muito distintas dos escalares, a ponto de as variáveis Perl terem sigilos diferentes, indicando seu tipo (
$
para escalares,@
matrizes,%
hashes) 1 . Não são regras de coerção entre esses tipos, assim que você pode escrever, por exemplo%foo = @bar
, mas muitos deles são bastante com perdas: por exemplo,$foo = @bar
atribui o comprimento da matriz@bar
para$foo
, não seu conteúdo. (Além disso, existem alguns outros tipos estranhos, como botões de escrever e identificadores de E / S, que você não costuma ver expostos.)Além disso, uma pequena brecha nesse belo design é a existência de tipos de referência, que são um tipo especial de escalares (e que podem ser distinguidos dos escalares normais, usando o
ref
operador). É possível usar referências como escalares normais, mas seus valores numéricos / de seqüência de caracteres não são particularmente úteis e tendem a perder sua referência especial se você os modificar usando operações escalares normais. Além disso, qualquer variável Perl 2 pode serbless
editada em uma classe, transformando-a em um objeto dessa classe; o sistema de classes OO em Perl é um tanto ortogonal ao sistema de tipo primitivo (ou falta de tipografia) descrito acima, embora também seja "fraco" no sentido de seguir a digitação do patoparadigma. A opinião geral é que, se você verificar a classe de um objeto no Perl, estará fazendo algo errado.1 Na verdade, o sigil indica o tipo do valor que está sendo acessado, de modo que, por exemplo, o primeiro escalar na matriz
@foo
é indicado$foo[0]
. Veja perlfaq4 para mais detalhes.2 Objetos no Perl são (normalmente) acessados através de referências a eles, mas o que realmente é
bless
editado é a variável (possivelmente anônima) para a qual a referência aponta. No entanto, a bênção é de fato uma propriedade da variável, não de seu valor, por exemplo, atribuir a variável abençoada real a outra apenas fornece uma cópia superficial e não abençoada dela. Veja perlobj para mais detalhes.fonte
Além do que Eric disse, considere o seguinte código C:
Ao contrário de linguagens como Python, C #, Java ou outros enfeites, o texto acima é fracamente digitado porque perdemos informações de tipo. Eric apontou corretamente que em C # podemos contornar o compilador lançando, dizendo efetivamente: "Eu sei mais sobre o tipo dessa variável que você".
Mas, mesmo assim, o tempo de execução ainda verificará o tipo! Se a conversão for inválida, o sistema de tempo de execução a capturará e lançará uma exceção.
Com o apagamento do tipo, isso não acontece - as informações do tipo são jogadas fora. Um elenco
void*
em C faz exatamente isso. A esse respeito, o exposto acima é fundamentalmente diferente de uma declaração de método de C # comovoid f(Object x)
.(Tecnicamente, o C # também permite o apagamento de tipo por meio de código não seguro ou empacotamento.)
Isso é o mais fraco possível. Tudo o resto é apenas uma questão de verificação de tipo estático versus dinâmico, ou seja, da época em que um tipo é verificado.
fonte
void*
rompe as duas verificações de tipo. O apagamento de tipo genérico não, apenas evita as verificações em tempo de compilação. É exatamente como elencos explícitos (mencionados por Eric) a esse respeito.Um exemplo perfeito vem do artigo da Wikipedia sobre Strong Typing :
Geralmente, a digitação forte implica que a linguagem de programação impõe severas restrições à mistura que é permitida.
Digitação fraca
Digitação forte
Observe que uma linguagem de digitação fraca pode misturar tipos diferentes sem erros. Um idioma de tipo forte exige que os tipos de entrada sejam os tipos esperados. Em uma linguagem de texto forte, um tipo pode ser convertido (
str(a)
converte um número inteiro em uma string) ou cast (int(b)
).Tudo isso depende da interpretação da digitação.
fonte
Eu gostaria de contribuir para a discussão com minha própria pesquisa sobre o assunto, à medida que outros comentam e contribuem. Eu tenho lido suas respostas e seguido suas referências e encontrei informações interessantes. Como sugerido, é provável que a maior parte disso seja melhor discutida no fórum dos programadores, pois parece ser mais teórico do que prático.
Do ponto de vista teórico, acho que o artigo de Luca Cardelli e Peter Wegner, intitulado Sobre a compreensão de tipos, abstração de dados e polimorfismo, tem um dos melhores argumentos que já li.
Essa afirmação parece sugerir que a digitação fraca nos permitiria acessar a estrutura interna de um tipo e manipulá-la como se fosse outra coisa (outro tipo). Talvez o que possamos fazer com código não seguro (mencionado por Eric) ou com ponteiros apagados por tipo c mencionados por Konrad.
O artigo continua ...
Como tal, digitação forte significa ausência de erros de tipo, posso apenas assumir que digitação fraca significa o contrário: a provável presença de erros de tipo. Em tempo de execução ou tempo de compilação? Parece irrelevante aqui.
O engraçado é que, de acordo com essa definição, uma linguagem com fortes coerções de tipos como Perl seria considerada fortemente tipada, porque o sistema não está falhando, mas está lidando com os tipos, coagindo-os em equivalências apropriadas e bem definidas.
Por outro lado, eu poderia dizer que o subsídio
ClassCastException
eArrayStoreException
(em Java) eInvalidCastException
,ArrayTypeMismatchException
(em C #) indicaria um nível de tipagem fraca, pelo menos em tempo de compilação? A resposta de Eric parece concordar com isso.Em um segundo artigo chamado Typeful Programming fornecido em uma das referências fornecidas em uma das respostas nesta pergunta, Luca Cardelli investiga o conceito de violação de tipo:
Dessa forma, coerções de tipo como as fornecidas pelos operadores podem ser consideradas violações de tipo, mas, a menos que quebrem a consistência do sistema de tipos, podemos dizer que elas não levam a um sistema de tipo fraco.
Com base nisso, nem Python, Perl, Java ou C # são fracamente tipados.
Cardelli menciona dois tipos de difamação que considero muito bem casos de digitação realmente fraca:
Esse tipo de coisa possível em idiomas como C (mencionado por Konrad) ou através de código inseguro em .Net (mencionado por Eric) realmente implicaria digitação fraca.
Acredito que a melhor resposta até agora é a de Eric, porque a definição desses conceitos é muito teórica e, quando se trata de uma linguagem específica, as interpretações de todos esses conceitos podem levar a diferentes conclusões discutíveis.
fonte
A digitação fraca realmente significa que uma alta porcentagem de tipos pode ser implicitamente coagida, tentando adivinhar o que o codificador pretendia.
A digitação forte significa que os tipos não são coagidos ou, pelo menos, coagidos menos.
A digitação estática significa que os tipos de suas variáveis são determinados em tempo de compilação.
Muitas pessoas recentemente confundiram "manifestamente digitado" com "fortemente digitado". "Digitado manifestamente" significa que você declara explicitamente os tipos de suas variáveis.
O Python é tipicamente fortemente tipado, embora você possa usar quase qualquer coisa em um contexto booleano, e os booleanos possam ser usados em um contexto inteiro e você possa usar um número inteiro em um contexto flutuante. Não é digitado manifestamente, porque você não precisa declarar seus tipos (exceto o Cython, que não é inteiramente python, embora interessante). Também não é digitado estaticamente.
C e C ++ são tipicamente digitados, estaticamente e um pouco fortemente tipados, porque você declara seus tipos, os tipos são determinados em tempo de compilação e você pode misturar números inteiros e ponteiros, números inteiros e duplos, ou mesmo converter um ponteiro para um tipo em um ponteiro para outro tipo.
Haskell é um exemplo interessante, porque não é manifestamente digitado, mas também estático e fortemente tipado.
fonte
A digitação forte <=> fraca não é apenas sobre o continuum de quanto ou quão pouco dos valores são coagidos automaticamente pelo idioma de um tipo de dados para outro, mas quão forte ou fracamente o real valores são digitados. Em Python e Java, e principalmente em C #, os valores têm seus tipos definidos em pedra. No Perl, nem tanto - existem realmente apenas alguns tipos diferentes de valores para armazenar em uma variável.
Vamos abrir os casos um por um.
Pitão
No exemplo do Python
1 + "1"
, o+
operador chama o__add__
tipo for,int
fornecendo a string"1"
como argumento - no entanto, isso resulta em NotImplemented:Em seguida, o intérprete tenta o
__radd__
comando str:Como falha, o
+
operador falha com o resultadoTypeError: unsupported operand type(s) for +: 'int' and 'str'
. Como tal, a exceção não diz muito sobre digitação forte, mas o fato de o operador+
não coagir seus argumentos automaticamente para o mesmo tipo é um indicador do fato de que Python não é a linguagem de tipo mais fraco no continuum.Por outro lado, em Python
'a' * 5
é implementado:Isso é,
O fato de a operação ser diferente requer uma digitação forte - no entanto, o oposto de
*
coagir os valores a números antes de multiplicar ainda não necessariamente tornaria os valores digitados de maneira fraca.Java
O exemplo de Java
String result = "1" + 1;
funciona apenas porque, por uma questão de conveniência, o operador+
está sobrecarregado para seqüências de caracteres. O+
operador Java substitui a sequência pela criação de umStringBuilder
(consulte este ):Este é um exemplo de digitação muito estática, sem coerção real -
StringBuilder
tem um métodoappend(Object)
que é usado especificamente aqui. A documentação diz o seguinte:Onde
String.valueOf
entãoPortanto, este é um caso de absolutamente nenhuma coerção por parte da linguagem - delegando toda preocupação aos próprios objetos.
C #
De acordo com a resposta de Jon Skeet aqui , o operador
+
nem está sobrecarregado para astring
classe - semelhante ao Java, isso é apenas uma conveniência gerada pelo compilador, graças à digitação estática e forte.Perl
Como o perldata explica,
O Perl, no entanto, não possui um tipo de dados separado para números, booleanos, seqüências de caracteres, valores nulos,
undefined
s, referências a outros objetos etc. - ele só possui um tipo para todos esses, o tipo escalar; 0 é um valor escalar tanto quanto "0". Uma variável escalar que foi definida como uma string pode realmente se transformar em um número e, a partir daí, se comportar de maneira diferente de "apenas uma string", se for acessada em um contexto numérico. O escalar pode conter qualquer coisa no Perl, é o objeto que existe no sistema. enquanto em Python os nomes se referem apenas aos objetos, em Perl os valores escalares nos nomes são objetos alteráveis. Além disso, o sistema de tipo orientado a objetos é colado em cima disso: existem apenas três tipos de dados em perl - escalares, listas e hashes. Um objeto definido pelo usuário no Perl é uma referência (que é um ponteiro para qualquer um dos 3 anteriores)bless
distribuído para um pacote - você pode pegar esse valor e abençoá-lo com qualquer classe a qualquer momento que desejar.O Perl ainda permite que você altere as classes de valores por capricho - isso não é possível no Python, para criar um valor de alguma classe, você precisa construir explicitamente o valor pertencente a essa classe
object.__new__
ou similar. No Python, você não pode realmente mudar a essência do objeto após a criação; no Perl, você pode fazer qualquer coisa:assim, a identidade do tipo está fracamente ligada à variável e pode ser alterada através de qualquer referência em tempo real. De fato, se você fizer
\$another
não possui a identidade de classe, mesmo que\$val
ainda dê a referência abençoada.TL; DR
Há muito mais sobre digitação fraca no Perl do que apenas coerções automáticas, e é mais sobre o fato de que os tipos dos valores em si não são gravados em pedra, ao contrário do Python, que é uma linguagem de tipo dinâmico e muito fortemente tipada. Que python dá
TypeError
on1 + "1"
é uma indicação de que a linguagem é fortemente tipado, embora o contrário de fazer algo útil, como em Java ou C # não impede que eles, sendo línguas rigidez.fonte
Como muitos outros expressaram, toda a noção de digitação "forte" vs "fraca" é problemática.
Como arquétipo, o Smalltalk é digitado com muita força - sempre gera uma exceção se uma operação entre dois objetos for incompatível. No entanto, suspeito que poucos nesta lista chamariam o Smalltalk de uma linguagem fortemente tipada, porque é digitada dinamicamente.
Acho a noção de digitação "estática" versus "dinâmica" mais útil do que "forte" versus "fraca". Uma linguagem de tipo estaticamente possui todos os tipos descobertos em tempo de compilação, e o programador deve declarar explicitamente, caso contrário.
Contraste com um idioma digitado dinamicamente, onde a digitação é realizada em tempo de execução. Isso geralmente é um requisito para linguagens polimórficas, para que decisões sobre se uma operação entre dois objetos seja legal não precisam ser decididas pelo programador com antecedência.
Em linguagens polimórficas e de tipo dinâmico (como Smalltalk e Ruby), é mais útil pensar em um "tipo" como uma "conformidade com o protocolo". Se um objeto obedece a um protocolo da mesma forma que outro objeto, mesmo que os dois objetos não compartilhem nenhuma herança, mixins ou outro vodu, eles são considerados o mesmo "tipo" pelo sistema de tempo de execução. Mais corretamente, um objeto em tais sistemas é autônomo e pode decidir se faz sentido responder a uma mensagem específica referente a um argumento específico.
Deseja um objeto que possa dar alguma resposta significativa à mensagem "+" com um argumento de objeto que descreva a cor azul? Você pode fazer isso em idiomas tipificados dinamicamente, mas é problemático em idiomas tipicamente estatísticos.
fonte
Gosto da resposta de @Eric Lippert , mas para abordar a questão - linguagens fortemente tipadas geralmente têm conhecimento explícito dos tipos de variáveis em cada ponto do programa. Linguagens com tipos fracos não o fazem, portanto, elas podem tentar executar uma operação que talvez não seja possível para um tipo específico. Ele acha que a maneira mais fácil de ver isso é em uma função. C ++:
a
Sabe-se que a variável é do tipo string e qualquer operação incompatível será capturada no momento da compilação.Pitão:
A variável
a
pode ser qualquer coisa e podemos ter um código que chama um método inválido, que só será pego em tempo de execução.fonte