Tipos em Lisp e Scheme

10

Vejo agora que o Racket tem tipos. À primeira vista, parece ser quase idêntico à digitação de Haskell. Mas o CLOS de Lisp está cobrindo parte do espaço que os tipos de Haskell cobrem? Criar um tipo Haskell muito estrito e um objeto em qualquer linguagem OO parece vagamente semelhante. Só que eu bebi um pouco do kool-aid Haskell e estou totalmente paranóica que, se eu for pela estrada Lisp, serei ferrado devido à digitação dinâmica.

user2054900
fonte

Respostas:

6

O sistema do tipo CL é mais expressivo que o do Haskell, por exemplo, você pode ter um tipo (or (integer 1 10) (integer 20 30))para um valor 1,2,...9,10,20,21,...,30.

No entanto, os compiladores Lisp não forçam a compreensão do tipo de segurança na garganta, para que você possa ignorar as "anotações" - por seu próprio risco .

Isso significa que você pode escrever Haskell no Lisp (por assim dizer) declarando todos os tipos de valor e cuidadosamente certificando-se de que todos os tipos necessários sejam inferidos, mas é mais fácil usar o Haskell em primeiro lugar.

Basicamente, se você deseja uma digitação estática forte, use Haskell ou OCaml, se desejar uma digitação dinâmica forte, use Lisp. Se você deseja uma digitação estática fraca, use C, se desejar uma digitação dinâmica fraca, use Perl / Python. Cada caminho tem suas vantagens (e fanáticos) e desvantagens (e detratores); portanto, você se beneficiará do aprendizado de todos eles.

sds
fonte
19
Qualquer pessoa que use termos como "segurança de tipo de força na garganta" não entende o que é segurança de tipo ou por que é útil.
Mason Wheeler
11
@MasonWheeler: qualquer pessoa que faça uma conclusão abrangente de uma única frase se encontrará errada com mais frequência do que de outra forma. Como, por exemplo, neste caso.
sds 17/02/2013
4
Como o tópico é um idioma, o termo "garganta abaixo" é uma imagem apropriada e adequada.
Luser droog
11
@KChaloux: Eu quis dizer "expressivo", conforme esclarecido no exemplo.
sds
4
Você tem tudo ao contrário. A digitação dinâmica é um caso especial de digitação estática, em que você força o programador a usar 1 tipo para tudo. Eu posso fazer o mesmo (verbalmente) na maioria das linguagens estaticamente declaradas, declarando cada variável como tipo Objectou seja qual for a raiz da árvore de tipos. Isso é menos expressivo, porque você está privado da opção de dizer que determinadas variáveis ​​podem conter apenas determinados valores.
Doval
5

A raquete digitada é muito diferente de Haskell. Os sistemas de tipos no Lisp e Scheme, e de fato sistemas de sistemas em linguagens tradicionalmente não tipadas em geral, têm uma meta fundamental que outros sistemas de tipos não possuem - interoperando com o código não tipificado existente . A raquete digitada, por exemplo, introduziu novas regras de digitação para acomodar vários idiomas da raquete. Considere esta função:

(define (first some-list)
  (if (empty? some-list)
      #f
      (car some-list)))

Para listas não vazias, isso retorna o primeiro elemento. Para listas vazias, isso retorna false. Isso é comum em idiomas sem tipo; um idioma digitado usaria algum tipo de invólucro como Maybeou geraria um erro no caso vazio. Se quisermos adicionar um tipo a essa função, que tipo deve ser usado? Não é [a] -> a(na notação Haskell), porque pode retornar falso. Também não é [a] -> Either a Boolean, porque (1) sempre retorna false no caso vazio, não é um booleano arbitrário e (2) um dos dois tipos envolve elementos Lefte false dentro Righte exige que você "desembrulhe os dois" para chegar ao elemento real. Em vez disso, o valor retorna uma verdadeira união- não há construtores de quebra automática, ele simplesmente retorna um tipo em alguns casos e outro tipo em outros casos. No raquete digitado, isso é representado com o construtor de tipo de união:

(: first (All (A) (-> (Listof A) (U A #f))))
(define (first some-list)
  (if (empty? some-list)
      #f
      (car some-list)))

O tipo (U A #f)afirma que a função pode retornar um elemento da lista ou false sem nenhuma Eitherinstância de quebra automática. O verificador de tipos pode inferir que some-listé do tipo (Pair A (Listof A))ou da lista vazia e, além disso, infere que, nos dois ramos da instrução if , é conhecido qual deles é o caso . O verificador de tipos sabe que na (car some-list)expressão, a lista deve ter o tipo (Pair A (Listof A))porque a condição if garante isso. Isso é chamado digitação de ocorrência e foi projetado para facilitar a transição do código não digitado para o código digitado.

O problema é a migração. Existe uma tonelada de código de raquete não digitado por aí, e o Typed Racket não pode apenas forçá-lo a abandonar todas as suas bibliotecas não tipificadas favoritas e passar um mês adicionando tipos à sua base de código, se você quiser usá-la. Esse problema se aplica sempre que você adiciona tipos gradualmente a uma base de código existente, consulte TypeScript e seu tipo Qualquer para um aplicativo javascript dessas idéias.

Um sistema de tipo gradual deve fornecer ferramentas para lidar com idiomas comuns não digitados e interagir com o código não digitado existente. Caso contrário, será bastante doloroso. Consulte "Por que não estamos mais usando o Core.typed" para um exemplo do Clojure.

Jack
fonte
11
Em economia, isso é conhecido como Lei de Gresham : dinheiro ruim expulsa o bem. Também tende a encontrar aplicações em engenharia. Quando você tem dois subsistemas teoricamente equivalentes em seu sistema, muitas vezes o pior causará muitos problemas para fazer o melhor que vale a pena usar, como estamos vendo aqui.
Mason Wheeler
"um exemplo de criação de um sistema do tipo que": esta frase não está completo
coredump
@MasonWheeler O pior é, deixe-me adivinhar, a verificação dinâmica de tipo. Então, assim que você o apresenta, não vale a pena realizar algumas análises estáticas?
Coredump 26/05
11
@coredump - A digitação gradual vale a pena, a menos que você interaja mal com o código não digitado. O tipo Any, no TypeScript, por exemplo, apenas pede que o datilógrafo desista, basicamente descartando os benefícios do sistema de tipos, pois pode causar a propagação de um valor ruim por grandes quantidades de código verificado estaticamente. O raquete digitado usa contratos e limites do módulo para evitar esse problema.
26416 Jack
11
@coredump Leia o artigo Clojure. A digitação dinâmica, principalmente com todo o código de terceiros que não tinha tipos estáticos disponíveis, realmente estragou tudo as tentativas de melhorar sua base de código com tipos estáticos.
Mason Wheeler