Crítica de OCaml: ¿sigue siendo válida?


15

Soy un novato completo con OCaml. Recientemente me topé con esta página que enumera una buena cantidad de críticas hacia OCaml.

Al ver que la página es bastante antigua (2007): ¿cuál de los puntos de viñetas enumerados allí todavía es cierto hoy en día? Por ejemplo: ¿sigue siendo cierto que es imposible imprimir un objeto genérico?

Quiero dejar en claro que no estoy buscando una discusión sobre las opiniones expresadas allí. Me pregunto si la información enumerada, como el hecho de que los enteros se desbordan sin advertencias, sigue siendo correcta para las versiones más recientes de OCaml


55
Estoy votando para cerrar esta pregunta como fuera de tema porque meta.programmers.stackexchange.com/questions/6417/…
Philipp

3
Hay una bifurcación donde esos podrían haberse solucionado: tryfsharp.org
Den

77
Por supuesto, la opinión en ese ensayo que vinculó es válida, pero si estas críticas son relevantes para usted es un asunto completamente diferente. Tenga en cuenta que el autor proviene de un fondo Common Lisp y, por lo tanto, tiene valores Lispish. Si adopta otro enfoque de OCaml que no lo compara con Lisp (por ejemplo, "OCaml es Haskell para simples mortales" o "Si C fuera un lenguaje funcional, tendría OCaml"), lo encontrará mucho más satisfactorio. A pesar de todos sus defectos, OCaml es un gran lenguaje y, sin embargo, te animo a que lo hagas.
amon

55
Hasta donde sé, no hay forma de detectar el desbordamiento de enteros en el hardware , por lo que necesitaría insertar comprobaciones de tiempo de ejecución por todas partes para detectarlo; pero el autor rechazó el uso de "bignum" en función del rendimiento! La queja sobre la escritura estática equivale a decir que los cinturones de seguridad son malos porque podrías pensar que no puedes morir en un accidente automovilístico. La queja sobre la inmutabilidad del módulo dice que quiere hacer parches de cosas, una práctica antimodular y propensa a errores. El "zoológico de tipo pequeño" no tiene nada que ver con la inferencia de tipos. Está claro dónde están sus prejuicios .
Doval

2
@Doval: Por supuesto, puede detectar el desbordamiento en el hardware. Tratar con eso, sin embargo, es una cuestión de software.
Mason Wheeler

Respuestas:


13

Este artículo se discute en varios lugares:

Para resumir: sí, OCaml no es un Lisp, y no, no es perfecto (¿qué significa eso?). No creo que los puntos mencionados en la publicación del blog sean relevantes para los programadores del día a día de O'Caml.

Después de haber estudiado O'Caml, creo que es un lenguaje interesante que puede ayudarlo a crear programas en los que ni siquiera se atrevería a escribir, por ejemplo, C / C ++ / Java: por ejemplo, eche un vistazo a Frama-C .

Para una descripción actualizada de O'Caml, le animo a que lea sobre sus características : el lenguaje promueve fuertes técnicas de verificación de tipos estáticos que permiten que las implementaciones se centren en la producción de tiempos de ejecución performantes, pero seguros.

Importante : no soy un experto en OCaml: si eres uno de ellos y ves que escribí algo terriblemente mal, corrígeme. Editaré esta publicación en consecuencia.

Comprobación de tipo estático

  • Falsa sensación de seguridad

    Esto es cierto, pero obvio.

    La escritura estática le proporciona pruebas en las que puede confiar sobre un subconjunto de las propiedades de su programa. A menos que acepte ir todo formal, un programa promedio (que no sea de juguete) estará sujeto a errores de error de programación que solo se pueden observar en tiempo de ejecución.

    Es entonces cuando se pueden aplicar técnicas de verificación dinámica: el compilador OCaml tiene marcas para generar ejecutables con información de depuración, y así sucesivamente ... O puede generar código que confía ciegamente en el programador y borra la información de tipo tanto como sea posible. Los programadores que desean programas robustos deben implementar comprobaciones dinámicas explícitamente.

    Lo mismo se aplica, por ejemplo, a Common Lisp, pero se invierte: los tipos dinámicos primero, con las declaraciones de tipo opcionales y las directivas del compilador en segundo lugar.

  • Pocos tipos básicos

    Todavía se aplica: el idioma principal no ha cambiado (o no de forma espectacular).

  • Desbordamiento de enteros silencioso

    Esta es la norma en la mayoría de los idiomas en los que el desbordamiento de enteros se verifica manualmente. No conozco ninguna biblioteca que escriba operaciones de verificación para verificar si se puede producir un desbordamiento.

  • Módulo de inmutabilidad

    1. El autor menciona a Functors pero no veo cómo no se puede implementar su ejemplo. Al leer el capítulo Módulos de primera clase de https://realworldocaml.org , parece que los módulos se pueden usar para componer y construir nuevos módulos. Por supuesto, la modificación de un módulo existente requiere la modificación del código fuente, pero nuevamente, esto no es inusual entre los lenguajes de programación.

    2. " Semánticamente , las funciones se compilan EN LÍNEA"

    El hilo de reddit anterior no está de acuerdo, diciendo que los enlaces se resuelven en el momento del enlace. Sin embargo, este es un detalle de implementación y creo que el énfasis semántico se relaciona con la forma en que se resuelven las funciones. Ejemplo:

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

    El programa anterior se compila y, cuando se ejecuta, devuelve 5, porque gse define con la primera versión de f, como si la función de llamada gincluyera la llamada f. Esto no es "malo", por cierto, es coherente con las reglas de sombreado del nombre de O'Caml.

    Para resumir : sí, los módulos son inmutables . Pero también son compostables .

  • El polimorfismo provoca errores de tipo en tiempo de ejecución

    No puedo reproducir el error mencionado. Sospecho que es un error del compilador.

Sin macros

De hecho, no hay macros sino preprocesadores (OcamlP4, OcamlP5, ...).

Suckiness de lenguaje menor

  • Campo de registro nombrando infierno

    Es cierto, pero debe usar módulos:

    1. Dos campos de dos registros tienen la misma etiqueta en OCaml
    2. Resolviendo nombres de campo
  • Sintaxis

    Todavía se aplica (pero realmente, esto es solo sintaxis).

  • Sin polimorfismo

    Todavía se aplica, pero de alguna manera hay personas que prefieren eso en lugar de la torre numérica de Lisp (no sé por qué). Supongo que ayuda con la inferencia de tipos.

  • Conjuntos de funciones inconsistentes

    Vea el proyecto de baterías incluidas de OCaml . En particular, BatArray , por ejemplo, map2para matrices.

  • No hay variables dinámicas.

    Puede ser implementado:

    1. http://okmij.org/ftp/ML/dynvar.txt
    2. http://okmij.org/ftp/ML/index.html#dynvar
  • Los argumentos opcionales apestan

    Por restricción de idioma, no puede mezclar argumentos opcionales y palabras clave en Common Lisp. ¿Significa que apesta? (por supuesto, esto se puede cambiar con macros (ver, por ejemplo, mi respuesta )). Consulte la documentación de O'Caml para ver argumentos opcionales y nombrados en O'Caml.

  • Inconsistencia de aplicación de argumento parcial

    No creo que esto sea realmente molesto en la práctica.

  • Legibilidad de la aritmética

    Se mantiene, pero puede usar R o Python para problemas numéricos si lo prefiere.

  • Nombre silencioso resolución de conflictos

    Todavía se aplica, pero tenga en cuenta que esto está bien documentado.

  • No hay entrada / salida de objeto

    Aún se aplica.

Implementación, bibliotecas

Estos siguen cambiando todos los días: no hay una respuesta definitiva.

Finalmente,

"Deberías probar OCaml (o, mejor aún, Haskell) incluso si crees que apesta y no estás planeando usarlo. Sin él, tu educación en Informática está incompleta, al igual que está incompleta sin algo de Lisp y C (o , mejor aún, Asamblea) exposición ".

... todavía se aplica.


Gracias, echaré un vistazo a los enlaces, pero en realidad no estoy preguntando si esas opiniones están justificadas. Estoy preguntando si los hechos aún se mantienen. Por ejemplo: ¿hay alguna forma de imprimir algún tipo de datos algebraicos? ¿Sigue siendo exacto que los enteros se desborden sin advertencias? ¿Existe hoy una manera de operar en el archivo y deshacerse de ellos sin tener que escribir cada vez que repetitivo para tratar el cierre de archivos en caso de errores?
Andrea

@ Andrea edité mi respuesta.
coredump

1
"... hay personas que prefieren eso en lugar de la torre numérica de Lisp (no sé por qué). Supongo que ayuda con la inferencia de tipos". Bingo. El sistema de tipos de SML y OCaml requiere que cada expresión tenga un solo tipo. La sobrecarga de SML de operadores matemáticos es una excepción incorporada en el lenguaje. Esto también es cierto para Haskell; habilitar ese tipo de sobrecarga fue la motivación detrás de las clases de tipos. El problema es que solo puede tener una instancia de clase de tipo por tipo. Tampoco puede convertir ciegamente un entero a un flotante de igual tamaño: un flotante de 64 bits solo tiene 54 bits de precisión.
Doval

@Doval ¿Qué tal algo como escribir la torre numérica para Racket? ¿Podemos imaginar una biblioteca OCaml que explote las clases de tipos o los Tipos de datos algebraicos generalizados (GADT) para proporcionar operadores matemáticos polimórficos? Con respecto a la conversión: no todas las operaciones son posibles, pero algunas son y podrían escribirse.
coredump

2
@Doval: "Re: operadores matemáticos polimórficos, no creo que haya ninguna manera sin implementar las clases de tipos de Haskell". F # tiene operadores matemáticos polimórficos sin clases de tipos.
Jon Harrop

7

Al ver que la página es bastante antigua (2007): ¿cuál de los puntos de viñetas enumerados allí todavía es cierto hoy?

  • Falso sentido de seguridad . Esto no tiene sentido.

  • Pocos tipos básicos . OCaml ahora tiene bytes y matrices de bytes, pero no tiene cadenas unicode integradas, enteros de 16 bits, enteros sin signo, flotantes de 32 bits, vectores o matrices. Las bibliotecas de terceros proporcionan algunos de estos.

  • Desbordamiento de enteros silencioso . Sin cambios, pero nunca fue un problema.

  • Módulo de inmutabilidad . Su recomendación de que las funciones y los módulos deberían ser mutables es un retroceso sombrío para Lisp y una muy mala idea. Puede reemplazar módulos usando includesi lo desea, pero no puede mutarlos, por supuesto.

  • El polimorfismo provoca errores de tipo en tiempo de ejecución . Este es un gran problema con OCaml y no se ha solucionado. A medida que sus tipos evolucionan, la igualdad polimórfica, la comparación y el hash comenzarán a fallar cuando encuentren tipos como funciones y la depuración del problema es muy difícil. F # tiene una gran solución para este problema.

  • No hay macros . Irónicamente, cuando escribió este OCaml en realidad tenía soporte completo para macros, pero ahora han decidido retirar la función.

  • Envolturas . Este fue un problema real y no se ha solucionado. Todavía no hay una try ... finallyconstrucción en el lenguaje OCaml y no hay un contenedor que la implemente en stdlib.

  • Lugares . Sin cambios pero sin problemas.

  • Campo de registro nombrando el infierno . Estructura tu código correctamente usando módulos.

  • Sintaxis . Sin cambios pero sin problemas.

  • Sin polimorfismo . Esto no tenía sentido cuando lo escribió y nada ha cambiado.

  • Conjuntos de funciones inconsistentes . OCaml todavía no tiene una consfunción. Esta bien. No quiero cosas de Lisp en mi idioma, gracias.

  • No hay variables dinámicas . Fue algo bueno de OCaml. Todavía es algo bueno de OCaml.

  • Los argumentos opcionales apestan . Argumentos opcionales rock. Insulté a Microsoft para que agregara argumentos opcionales a F #.

  • Inconsistencia de aplicación de argumento parcial . Eh?

  • Legibilidad aritmética . Esto ha cambiado desde que dejé de usar OCaml ~ hace 8 años. Aparentemente ahora puedes hacerlo Int64.((q * n - s * s) / (n - 1L)).

  • Nombre silencioso resolución de conflictos . Él estaba tratando de hacer un desarrollo de software completo en REPL como lo haría en Lisp. No hagas eso en OCaml. Utilice los archivos y la compilación por lotes recurriendo al REPL solo para probar, ejecutar código desechable y computación técnica interactiva.

  • Orden de evaluación . Esto estaba mal cuando lo escribió. El orden de evaluación no está definido en OCaml.

  • No hay entrada / salida de objeto . Citó una biblioteca de terceros que ya resolvió este "problema".

  • El compilador se detiene después del primer error . Eh?

  • No hay seguimiento de pila para ejecutables compilados de forma nativa . Fijo.

  • El depurador apesta . Nunca usé el depurador. La comprobación de tipo estático detecta casi todos mis errores.

  • GC apesta . Encontré que el GC de OCaml es excelente, excepto por un problema importante: el bloqueo global impide la programación paralela.

  • No hay declaraciones directas implícitas . La recursividad mutua es explícita por diseño en todos los NM. Lo único extraño es que las typedefiniciones son recursivas por defecto, mientras que los letenlaces no son recursivos por defecto.

  • La ronda de funciones está ausente . OCaml todavía tiene un stdlib básico pero bibliotecas de terceros como Jane St's Core roundy amigos.

  • Listas . List.mapTodavía no es recursivo de cola. Envié parches para corregir errores graves como este y tuve que esperar años antes de que aparecieran en los lanzamientos. Las listas siguen siendo inmutables, por supuesto. Y así deberían ser.

  • Velocidad . Creo que los tiempos de compilación para grandes variantes polimórficas han sido corregidos.

  • Patronaje a juego . Un triunfo de la esperanza sobre la realidad. La comunidad de Lisp no ha podido hacer esto. De ahí mi décima regla: cualquier programa Lisp suficientemente complicado contiene una implementación ad hoc, especificada informalmente y llena de errores de la mitad del compilador de coincidencia de patrones de OCaml.

Por ejemplo: ¿sigue siendo cierto que es imposible imprimir un objeto genérico?

Cuando escribió que no podías simplemente hacer:

print value

pero podría invocar la bonita impresora desde el nivel superior como una llamada a la biblioteca, dándole la información de tipo necesaria. Y había una macro que puedes usar para anotar estructuras de datos para tener impresoras bonitas autogeneradas.


Coincidencia de patrones: el código fuente de OCaml y optima hacen referencia al mismo documento: "Optimización de la coincidencia de patrones". Yo diría que aquí no se puede aplicar de manera realista "ad-hoc", "lleno de errores" ni "especificado informalmente". "F # tiene una gran solución para este problema": me gustaría ver un poco más de detalles sobre esto, si es posible. La respuesta es buena, pero maldecir cuando se habla consda un mal tono (el artículo original es una queja, pero no es necesario copiarlo).
coredump

2
@coredump: "Me gustaría ver un poco más de detalles sobre esto, si es posible". F # tiene polimorfismo ad-hoc definido por el usuario en ciertos operadores y funciones. Por lo tanto, puede usar +en ints, flotantes y complejos, pero también puede definir sus propios tipos y agregar una sobrecarga para +trabajar en su tipo. Esto proporciona la brevedad y la legibilidad de Lisp o Haskell con el rendimiento previsiblemente bueno de SML u OCaml, logrando algo que ningún otro lenguaje hace.
Jon Harrop

@coredump: "El código fuente de OCaml y optima hacen referencia al mismo documento". Las técnicas descritas en ese documento se basan completamente en el sistema de tipo ML. Un sistema de tipos que Lisp no tiene. Optima no hace ni puede hacer mucho de lo que se describe en ese documento. Solo mire la sección 4.2 "Uso de la información de exhaustividad". No hay información de exhaustividad en Lisp porque no hay tipos de variantes. Por ejemplo, OCaml elige entre saltos anidados o una tabla de despacho en función del número de hojas, pero esa información es desconocida en Lisp.
Jon Harrop

Lisp y OCaml están diseñados para diferentes cosas (por ejemplo, dinamismo, programación basada en imágenes). Pero Lisp tiene un sistema de tipos, diferente de Hindley-Milner, y las implementaciones lo explotan durante la compilación. Mire esta sesión de SBCL con ejemplos de la sección 4.2 del documento. El compilador ya verifica la exhaustividad a partir de la inferencia de tipos y las declaraciones. Optima podría agregar un código específico de implementación para la macroexpansión en tiempo de compilación (o SBOP VOP), para tener otras estrategias, pero no hay suficientes incentivos para hacerlo.
coredump

¿Cómo se aplica eso al tipo de datos algebraico definido por el usuario?
Jon Harrop
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.