Crítica ao OCaml: ainda é válida?

15

Eu sou um novato completo com OCaml. Recentemente, deparei com esta página listando uma boa quantidade de críticas ao OCaml.

Visto que a página é bastante antiga (2007): quais dos pontos de bala listados ainda são verdadeiros hoje? Por exemplo: ainda é verdade que é impossível imprimir um objeto genérico?

Quero deixar claro que não estou buscando uma discussão das opiniões expressas nela. Estou perguntando se as informações listadas, como o fato de números inteiros excederem sem avisos, ainda estão corretas para versões mais recentes do OCaml

Andrea
fonte
5
Estou votando para encerrar esta pergunta como off-topic porque meta.programmers.stackexchange.com/questions/6417/…
Philipp
3
Há uma bifurcação onde aqueles poderia ter sido fixado: tryfsharp.org
Den
7
É claro que a opinião nesse ensaio que você vinculou é válida, mas se essas críticas são relevantes para você é uma questão totalmente diferente. Observe que o autor vem de um plano de fundo Common Lisp e, portanto, possui valores Lispish. Se você adotar outra abordagem do OCaml do que compará-la com o Lisp (por exemplo, “OCaml é Haskell para meros mortais” ou “Se C fosse uma linguagem funcional, você teria o OCaml”), será muito mais satisfatório. Apesar de todas as suas falhas, o OCaml é uma ótima linguagem e eu encorajo você a se interessar por ela.
amon
5
Até onde eu sei, não há como detectar excesso de número inteiro no hardware ; portanto, você precisará inserir verificações de tempo de execução em todo o lugar para detectá-lo; mas o autor dispensou o uso de "bignum" com base no desempenho! A reclamação sobre a digitação estática equivale a dizer que os cintos de segurança são ruins porque você pode pensar que não pode morrer em um acidente de carro. A reclamação sobre a imutabilidade do módulo está dizendo que ele deseja monitorar as coisas - uma prática antimodular e propensa a erros. O "zoológico de tipo pequeno" não tem nada a ver com inferência de tipo. Está claro onde estão seus preconceitos .
D
2
@Doval: É claro que você pode detectar estouro no hardware. Lidar com isso, no entanto, é uma questão de software.
Mason Wheeler

Respostas:

13

Este artigo é discutido em vários locais:

Para resumir: sim, o OCaml não é um Lisp e não, não é perfeito (o que isso significa?). Não acho que os pontos mencionados na postagem do blog sejam relevantes para os programadores do dia-a-dia do O'Caml.

Tendo estudado O'Caml, acho que é uma linguagem interessante que pode ajudá-lo a criar programas nos quais você nem ousaria escrever, digamos, C / C ++ / Java: por exemplo, dê uma olhada no Frama-C .

Para obter uma descrição atualizada de O'Caml, encorajo você a ler sobre seus recursos : a linguagem promove fortes técnicas de verificação de tipo estático que permitem que as implementações se concentrem na produção de tempos de execução com desempenho, ainda que seguros.

Importante : Não sou especialista em OCaml: se você é um deles e vê que escrevi algo terrivelmente errado, corrija-me. Vou editar este post de acordo.

Verificação de tipo estático

  • Falso senso de segurança

    Isso é verdade, mas óbvio.

    A digitação estática fornece provas em que você pode confiar em um subconjunto das propriedades do seu programa. A menos que você aceite ser formal, um programa médio (sem brinquedos) estará sujeito a erros de programação que podem ser testemunhados apenas em tempo de execução.

    É quando as técnicas de verificação dinâmica podem ser aplicadas: o compilador OCaml possui sinalizadores para gerar executáveis ​​com informações de depuração e assim por diante ... Ou pode gerar código que confia cegamente no programador e apaga as informações de tipo o máximo possível. Programadores que desejam programas robustos devem implementar verificações dinâmicas explicitamente.

    O mesmo se aplica a, por exemplo, Common Lisp, mas invertido: tipos dinâmicos primeiro, com declarações de tipo opcionais e diretivas de compilador em segundo.

  • Alguns tipos básicos

    Ainda se aplica: o idioma principal não mudou (ou não drasticamente).

  • Estouro de número inteiro silencioso

    Essa é a norma na maioria dos idiomas em que o excesso de número inteiro é verificado manualmente. Não conheço nenhuma biblioteca que verifique operações de tipo para verificar se o estouro pode ocorrer.

  • Imutabilidade do módulo

    1. O autor menciona Functors, mas não vejo como o exemplo dele não pode ser implementado. Lendo o capítulo Módulos da Primeira Classe de https://realworldocaml.org , parece que os módulos podem ser usados ​​para compor e construir novos módulos. Obviamente, modificar um módulo existente requer modificação do código-fonte, mas, novamente, isso não é incomum entre linguagens de programação.

    2. " Semântica , funções são compiladas INLINE"

    O tópico do reddit acima discorda, dizendo que a ligação é resolvida no momento do link. No entanto, esse é um detalhe de implementação e acho que o enfatizado Semanticamente se relaciona à maneira como as funções são resolvidas. Exemplo:

     let f x y = x + y ;;
     let g a b = f b a ;;
     let f x y = x * y ;;
     exit (g 2 3) ;;
    

    O programa acima compila e, quando executado, retorna 5, porque gé definido com a primeira versão do f, exatamente como se a função de chamada gincluísse a chamada f. Isso não é "ruim", a propósito, é apenas consistente com as regras de sombreamento do nome de O'Caml.

    Resumindo : sim, os módulos são imutáveis . Mas eles também são compostáveis .

  • Polimorfismo causa erros de tipo em tempo de execução

    Não consigo reproduzir o erro mencionado. Eu suspeito que seja um erro do compilador.

Sem macros

De fato, não há macros, mas pré-processadores (OcamlP4, OcamlP5, ...).

Suckiness menor da língua

  • Campo de registro nomeando o inferno

    Verdadeiro, mas você deve usar os módulos:

    1. Dois campos de dois registros têm o mesmo rótulo no OCaml
    2. Resolvendo nomes de campos
  • Sintaxe

    Ainda se aplica (mas, na verdade, isso é apenas sintaxe).

  • Sem polimorfismo

    Ainda se aplica, mas de alguma forma existem pessoas que preferem isso em vez da torre numérica de Lisp (não sei por que). Suponho que ajude com inferência de tipo.

  • Conjuntos de funções inconsistentes

    Veja o projeto de baterias incluídas do OCaml . Em particular, BatArray , por exemplo, de map2matrizes.

  • Nenhuma variável dinâmica

    Pode ser implementado:

    1. http://okmij.org/ftp/ML/dynvar.txt
    2. http://okmij.org/ftp/ML/index.html#dynvar
  • Argumentos ~ opcionais são péssimos

    Por restrição de idioma, você não pode misturar argumentos opcionais e de palavras-chave no Common Lisp. Isso significa que é uma merda? (claro, isso pode ser alterado com macros (veja, por exemplo, minha resposta )). Consulte a documentação de O'Caml para argumentos opcionais e nomeados em O'Caml.

  • Inconsistência de aplicativo de argumento parcial

    Não acho que isso seja realmente irritante na prática.

  • Legibilidade da aritmética

    É válido, mas você pode usar R ou Python para problemas numéricos, se preferir.

  • Resolução de conflitos de nomes silenciosos

    Ainda se aplica, mas observe que isso está bem documentado.

  • Nenhum objeto de entrada / saída

    Ainda se aplica.

Implementação, bibliotecas

Eles continuam mudando todos os dias: não há resposta definitiva.

Finalmente,

"Você deve experimentar o OCaml (ou, melhor ainda, Haskell), mesmo que pense que é péssimo e não planeja usá-lo. Sem ele, seu ensino em Ciência da Computação é incompleto, assim como é incompleto sem alguns Lisp e C (ou , melhor ainda, montagem) ".

... ainda se aplica.

coredump
fonte
Obrigado, vou dar uma olhada nos links, mas não estou realmente perguntando se essas opiniões são justificadas. Estou perguntando se os fatos ainda valem. Por exemplo: existe uma maneira de imprimir qualquer tipo de dados algébricos? Ainda é preciso que os números sejam excedidos sem avisos? Existe hoje uma maneira de operar em arquivos e descartá-los sem ter que escrever toda vez que o clichê para lidar com o fechamento de arquivos com erros?
26715 Andrea Andrea
@ Andrea eu editei minha resposta.
Coredump
1
"... há pessoas que preferem isso em vez da torre numérica de Lisp (não sei por que). Suponho que isso ajude na inferência de tipos." Bingo. O sistema de tipos SML e OCaml exige que cada expressão tenha apenas um tipo. A sobrecarga de operadores matemáticos da SML é uma exceção incorporada à linguagem. Isso também é verdade para Haskell; permitir esse tipo de sobrecarga foi a motivação por trás das classes de tipos. O problema é que você pode ter apenas uma instância de classe de tipo por tipo. Você também não pode converter cegamente um número inteiro em um número flutuante de tamanho igual - um número flutuante de 64 bits possui apenas 54 bits de precisão.
Doval
@Doval Que tal algo como Digitar a torre numérica para o Racket? Podemos imaginar uma biblioteca OCaml que exploraria classes de tipos ou GADT (Generalized Algebraic Data Types) para fornecer operadores matemáticos polimórficos? Em relação à conversão: nem todas as operações são possíveis, mas algumas são e podem ser digitadas.
Coredump
2
@Doval: "Re: operadores matemáticos polimórficos, não acho que exista nenhuma maneira de implementar as classes de tipo de Haskell". O F # possui operadores matemáticos polimórficos sem classes de tipo.
Jon Harrop
7

Visto que a página é bastante antiga (2007): quais dos pontos de bala listados ainda são verdadeiros hoje?

  • Falso senso de segurança . Isso não faz sentido.

  • Alguns tipos básicos . O OCaml agora possui matrizes de bytes e bytes, mas não possui cadeias unicode internas, números inteiros de 16 bits, números inteiros não assinados, flutuações de 32 bits, vetores ou matrizes. Bibliotecas de terceiros fornecem algumas delas.

  • Estouro Inteiro Silencioso . Inalterado, mas nunca foi um problema.

  • Imutabilidade do módulo . Sua recomendação de que funções e módulos devem ser mutáveis ​​é um retrocesso sombrio para o Lisp e uma péssima idéia. Você pode substituir os módulos usando, includese quiser, mas não pode modificá-los, é claro.

  • Polimorfismo causa erros de tipo em tempo de execução . Este é um grande problema com o OCaml e não foi corrigido. À medida que seus tipos evoluem igualdade polimórfica, a comparação e o hash começam a falhar quando encontram tipos como funções e a depuração do problema é muito difícil. O F # tem uma ótima solução para esse problema.

  • Sem macros . Ironicamente, quando ele escreveu isso, o OCaml realmente tinha suporte total para macros, mas agora eles decidiram retirar o recurso.

  • Wrappers . Este foi um problema real e não foi corrigido. Ainda não há try ... finallyconstrução na linguagem OCaml e nenhum wrapper implementando-a no stdlib.

  • Locais . Inalterado, mas sem problemas.

  • Campo de registro nomeando o inferno . Estruture seu código corretamente usando módulos.

  • Sintaxe . Inalterado, mas sem problemas.

  • Sem polimorfismo . Isso não fazia sentido quando ele escreveu e nada mudou.

  • Conjuntos de funções inconsistentes . O OCaml ainda não tem uma consfunção. Isso é bom. Não quero coisas de Lisp no meu idioma, obrigado.

  • Sem variáveis ​​dinâmicas . Foi uma coisa boa sobre o OCaml. Ainda é uma coisa boa sobre o OCaml.

  • Argumentos ~ opcionais são péssimos . Argumentos opcionais. Instalei a Microsoft a fazê-los adicionar argumentos opcionais ao F #.

  • Inconsistência de aplicativo de argumento parcial . Eh?

  • Legibilidade da aritmética . Isso mudou desde que parei de usar o OCaml ~ 8 anos atrás. Aparentemente agora você pode fazer Int64.((q * n - s * s) / (n - 1L)).

  • Resolução de conflito de nome silencioso . Ele estava tentando fazer um desenvolvimento de software completo no REPL, como você faria no Lisp. Não faça isso no OCaml. Use arquivos e compilação em lote recorrendo ao REPL apenas para teste, execução de código descartável e computação técnica interativa.

  • Ordem de avaliação . Isso estava errado quando ele escreveu. A ordem da avaliação é indefinida no OCaml.

  • Nenhum objeto de entrada / saída . Ele citou uma biblioteca de terceiros que já resolveu esse "problema".

  • O compilador para após o primeiro erro . Eh?

  • Nenhum rastreamento de pilha para executáveis ​​compilados nativamente . Fixo.

  • Depurador é uma merda . Eu nunca usei o depurador. A verificação de tipo estático captura quase todos os meus erros.

  • GC é uma merda . Eu achei o GC do OCaml excelente, exceto por um grande problema: o bloqueio global impede a programação paralela.

  • Nenhuma declaração direta implícita . A recursão mútua é explícita por design em todos os MLs. A única estranheza é que as typedefinições são recursivas por padrão, enquanto as letligações não são recursivas por padrão.

  • A rodada de funções está ausente . O OCaml ainda tem um stdlib básico, mas bibliotecas de terceiros como o Core de Jane St fornecem rounde amigos.

  • Listas . List.mapainda não é recursivo da cauda. Enviei patches para corrigir erros graves como esse e tive que esperar anos antes de aparecerem nos lançamentos. As listas ainda são imutáveis, é claro. E assim deveriam ser.

  • Velocidade . Acredito que os tempos de compilação para grandes variantes polimórficas foram corrigidos.

  • Correspondência de padrões . Um triunfo da esperança sobre a realidade. A comunidade Lisp falhou ao fazer isso. Daí a minha décima regra: qualquer programa Lisp suficientemente complicado contém uma implementação ad-hoc, especificada informalmente e cheia de erros, de metade do compilador de correspondência de padrões do OCaml.

Por exemplo: ainda é verdade que é impossível imprimir um objeto genérico?

Quando ele escreveu que você não podia simplesmente fazer:

print value

mas você pode chamar a impressora bonita do nível superior como uma chamada de biblioteca, fornecendo as informações de tipo necessárias. E havia uma macro que você pode usar para anotar estruturas de dados, a fim de ter lindas impressoras geradas automaticamente.

Jon Harrop
fonte
Correspondência de padrões: o código-fonte OCaml e o optima referenciam o mesmo documento: "Otimizando a correspondência de padrões". Eu diria que nem "ad-hoc" nem "especificado informalmente" podem ser aplicados aqui de forma realista. "O F # tem uma ótima solução para esse problema": eu realmente gostaria de ver um pouco mais de detalhes sobre isso, se possível. A resposta é boa, mas xingar ao falar consdá um tom ruim (o artigo original é um discurso retórico, mas você não precisa copiar isso).
Coredump
2
@coredump: "Eu realmente gostaria de ver um pouco mais de detalhes sobre isso, se possível". O F # possui polimorfismo ad-hoc definido pelo usuário em determinados operadores e funções. Portanto, você pode usar +ints, floats e complexos, mas também pode definir seus próprios tipos e adicionar uma sobrecarga para +trabalhar no seu tipo. Isso fornece à brevidade e legibilidade do Lisp ou Haskell o desempenho previsivelmente bom do SML ou OCaml, alcançando algo que nenhuma outra linguagem faz.
9136 Jon Harrop
@coredump: "O código-fonte do OCaml e o optima referenciam o mesmo artigo". As técnicas descritas nesse documento são construídas inteiramente sobre o sistema do tipo ML. Um sistema de tipos que o Lisp não possui. O Optima não faz e não pode fazer muito do que é descrito nesse artigo. Veja a seção 4.2 "Usando informações exaustivas". Não há informações exaustivas no Lisp porque não existem tipos de variantes. Por exemplo, o OCaml escolhe entre saltos aninhados ou uma tabela de despacho com base no número de folhas, mas essas informações são desconhecidas no Lisp.
precisa saber é o seguinte
Lisp e OCaml são projetados para coisas diferentes (por exemplo, dinamismo, programação baseada em imagem). Mas Lisp tem um sistema do tipo, diferente de Hindley-Milner, e implementações não explorá-lo durante a compilação. Veja esta sessão da SBCL com exemplos da seção 4.2 do documento. A exaustão já é verificada pelo compilador a partir de inferência e declarações de tipo. O Optima poderia adicionar código específico da implementação para a macroexpansão em tempo de compilação (ou SBCL VOPs), para ter outras estratégias, mas não há incentivos suficientes para isso.
Coredump
Como isso se aplica ao tipo de dados algébrico definido pelo usuário?
Jon Harrop 23/02