Haskell, Lisp y verbosidad
comparison language-comparisons (8)
A medida que continúo mi viaje de aprendizaje de Haskell, parece que una cosa que ayuda a "reemplazar" las macros es la capacidad de definir sus propios operadores de infijo y personalizar su precedencia y asociatividad. ¡Un poco complicado, pero un sistema interesante!
Para aquellos de ustedes que tienen tanto experiencia en Haskell como en Lisp, me llama la atención lo "agradable" (para usar un término horrible) escribir código en Haskell vs. Lisp.
Algunos antecedentes: ahora estoy aprendiendo a Haskell, habiendo trabajado anteriormente con Scheme y CL (y una pequeña incursión en Clojure). Tradicionalmente, podrías considerarme un fanático de los lenguajes dinámicos por la brevedad y rapidez que ofrecen. Rápidamente me enamoré de las macros Lisp, ya que me dio otra forma de evitar la verbosidad y la repetición.
Estoy encontrando a Haskell increíblemente interesante, ya que me está presentando formas de codificación que no sabía que existían. Definitivamente tiene algunos aspectos que parecen ayudar a lograr agilidad, como la facilidad para escribir funciones parciales. Sin embargo, estoy un poco preocupado por la pérdida de las macros de Lisp (supongo que las pierdo, a decir verdad, es posible que todavía no haya aprendido sobre ellas) y el sistema de tipado estático.
¿Alguien que haya hecho una buena cantidad de codificación en ambas mentes del mundo comentando cómo difieren las experiencias, cuál prefiere, y si dicha preferencia es situacional?
En Haskell puedes definir una función if, lo cual es imposible en LISP. Esto es posible debido a la pereza, que permite una mayor modularidad en los programas. Este artículo clásico: Why FP matters by John Hughes, explica cómo la pereza mejora la capacidad de compilación.
En cuanto a las macros, aquí hay una página que habla de ello: Hola Haskell, Goodbye Lisp . Explica un punto de vista donde las macros simplemente no son necesarias en Haskell. Viene con un breve ejemplo de comparación.
Ejemplo de caso en el que se requiere una macro LISP para evitar la evaluación de ambos argumentos:
(defmacro doif (x y) `(if ,x ,y))
Ejemplo de caso en el que Haskell no evalúa sistemáticamente ambos argumentos, sin la necesidad de algo como una definición macro:
doif x y = if x then (Just y) else Nothing
Y voilá
En primer lugar, no se preocupe por perder características particulares como el tipado dinámico. Como está familiarizado con Common Lisp, un lenguaje notablemente bien diseñado, asumo que es consciente de que un idioma no se puede reducir a su conjunto de características. Se trata de un todo coherente, ¿no?
En este sentido, Haskell brilla tan brillantemente como lo hace Common Lisp. Sus características se combinan para proporcionarle una forma de programación que hace que el código sea extremadamente corto y elegante. La falta de macros se mitiga de alguna manera mediante conceptos más elaborados (pero, de la misma manera, más difíciles de comprender y usar) como las mónadas y las flechas. El sistema de tipo estático se agrega a su poder en lugar de ponerse en su camino como lo hace en la mayoría de los lenguajes orientados a objetos.
Por otro lado, la programación en Haskell es mucho menos interactiva que Lisp, y la enorme cantidad de reflexión presente en lenguajes como Lisp simplemente no se ajusta a la visión estática del mundo que Haskell presupone. Los conjuntos de herramientas disponibles para usted son, por lo tanto, bastante diferentes entre los dos idiomas, pero difíciles de comparar entre sí.
Personalmente, prefiero la forma de programación de Lisp en general, ya que creo que se ajusta a la forma en que trabajo mejor. Sin embargo, esto no significa que usted deba hacerlo también.
Hay cosas geniales que puedes lograr en Lisp con macros que son engorrosas (si es posible) en Haskell. Tomemos como ejemplo la macro `memoize ''(ver el Capítulo 9 del PAIP de Peter Norvig). Con él, puede definir una función, digamos foo, y luego simplemente evaluar (memoize ''foo), que reemplaza la definición global de foo con una versión modificada. ¿Se puede lograr el mismo efecto en Haskell con funciones de orden superior?
Hay menos necesidad de metaprogramación en Haskell que en Common Lisp porque mucho se puede estructurar alrededor de mónadas y la sintaxis agregada hace que las DSL incorporadas parezcan menos parecidas a un árbol, pero siempre hay Template Haskell, como menciona ShreevatsaR , e incluso Liskell (Haskell semántica + Lisp sintaxis) si te gustan los paréntesis.
Respuesta corta:
- casi cualquier cosa que puedas hacer con macros se puede hacer con una función de orden superior (e incluyo mónadas, flechas, etc.), pero podría requerir más reflexión (pero solo la primera vez, y es divertido, y serás un mejor programador para ello), y
- el sistema estático es lo suficientemente general como para que nunca se interponga en su camino, y de alguna manera sorprendentemente "ayuda a lograr agilidad" (como dijo) porque cuando su programa se compila puede estar casi seguro de que es correcto, por lo que esta certeza le permite probar a cabo cosas que de otra manera temería probar - hay una sensación "dinámica" en la programación, aunque no es lo mismo que con Lisp.
[Nota: hay un " Template Haskell " que le permite escribir macros como en Lisp, pero estrictamente hablando nunca debería necesitarlo ]
Soy un programador de Common Lisp.
Después de haber probado con Haskell hace algún tiempo, mi objetivo personal era seguir con CL.
Razones:
- tipado dinámico (consulte Tipado dinámico vs. estático: un análisis basado en patrones de Pascal Costanza )
- argumentos opcionales y palabras clave
- sintaxis de lista homoicónica uniforme con macros
- sintaxis de prefijo (no es necesario recordar las reglas de precedencia)
- impuro y por lo tanto más adecuado para la creación rápida de prototipos
- poderoso sistema de objetos con protocolo meta-objeto
- estándar maduro
- amplia gama de compiladores
Haskell tiene sus propios méritos, por supuesto, y hace algunas cosas de una manera fundamentalmente diferente, pero para mí no es suficiente a largo plazo.