He leído el libro Learn You a Haskell hasta el punto en que presentan Mónadas y cosas como Just a. Estos son tan contraintuitivos para mí que me dan ganas de dejar de intentar aprenderlo.
Pero realmente quiero intentarlo.
Me pregunto si al menos puedo evitar los conceptos avanzados por un tiempo y simplemente comenzar a usar las otras partes del lenguaje para hacer muchas tareas prácticas con las partes más básicas de Haskell, es decir, funciones, E / S estándar, manejo de archivos, interfaz de base de datos y similares.
¿Es este un enfoque razonable para aprender a usar Haskell?
Respuestas:
Primero distingamos entre aprender los conceptos abstractos y aprender ejemplos específicos de ellos.
No vas a llegar muy lejos ignorando todos los ejemplos específicos, por la simple razón de que son completamente ubicuos. De hecho, las abstracciones existen en gran parte porque unifican las cosas que estaría haciendo de todos modos con los ejemplos específicos.
Las abstracciones en sí mismas, por otro lado, son ciertamente útiles , pero no son inmediatamente necesarias. Puedes llegar bastante lejos ignorando por completo las abstracciones y simplemente usando los diferentes tipos directamente. Eventualmente querrás entenderlos, pero siempre puedes volver a ello más tarde. De hecho, casi puedo garantizar que si haces eso, cuando vuelvas a hacerlo, te darás una palmada en la frente y te preguntarás por qué pasaste todo ese tiempo haciendo las cosas de la manera más difícil en lugar de usar las prácticas herramientas de uso general.
Toma
Maybe a
como ejemplo. Es solo un tipo de datos:Es todo menos autodocumentado; Es un valor opcional. O tienes "solo" algo de tipo
a
o no tienes nada. Digamos que tiene una función de búsqueda de algún tipo, que vuelveMaybe String
a representar la búsqueda de unString
valor que puede no estar presente. Entonces, el patrón coincide en el valor para ver cuál es:¡Eso es todo!
Realmente, no hay nada más que necesites. No
Functor
soMonad
s ni nada más. Esos expresan formas comunes de usarMaybe a
valores ... pero son solo expresiones idiomáticas, "patrones de diseño", como quieran llamarlo.El único lugar en el que realmente no puedes evitarlo es
IO
, pero de todos modos es una misteriosa caja negra, por lo que no vale la pena tratar de entender lo que significa comoMonad
o lo que sea.De hecho, aquí hay una hoja de trucos para todo lo que realmente necesita saber
IO
por ahora:Si algo tiene un tipo
IO a
, eso significa que es un procedimiento que hace algo y escupe una
valor.Cuando tienes un bloque de código usando la
do
notación, escribe algo como esto:... significa ejecutar el procedimiento a la derecha de
<-
, y asignar el resultado al nombre a la izquierda.Mientras que si tienes algo como esto:
... significa asignar el valor de la expresión simple (no un procedimiento) a la derecha del
=
nombre a la izquierda.Si coloca algo allí sin asignar un valor, así:
... significa ejecutar un procedimiento e ignorar todo lo que devuelve. Algunos procedimientos tienen el tipo
IO ()
--el()
tipo es una especie de marcador de posición que no dice nada, por lo que simplemente significa que el procedimiento hace algo y no devuelve un valor. Algo así como unavoid
función en otros idiomas.Ejecutar el mismo procedimiento más de una vez puede dar resultados diferentes; Esa es la idea. Es por eso que no hay forma de "eliminar" el
IO
valor de un valor, porque algo enIO
no es un valor, es un procedimiento para obtener un valor.La última línea en un
do
bloque debe ser un procedimiento simple sin asignación, donde el valor de retorno de ese procedimiento se convierte en el valor de retorno para todo el bloque. Si desea que el valor de retorno use algún valor ya asignado, lareturn
función toma un valor simple y le brinda un procedimiento sin operación que devuelve ese valor.Aparte de eso, no tiene nada de especial
IO
; estos procedimientos son en realidad valores simples en sí mismos, y puede pasarlos y combinarlos de diferentes maneras. Es solo cuando se ejecutan en undo
bloque llamado en algún lugarmain
que hacen algo.Entonces, en algo como este programa de ejemplo completamente aburrido y estereotipado:
... puedes leerlo como un programa imprescindible. Estamos definiendo un procedimiento llamado
hello
. Cuando se ejecuta, primero ejecuta un procedimiento para imprimir un mensaje preguntando su nombre; a continuación, ejecuta un procedimiento que lee una línea de entrada y asigna el resultado aname
; entonces asigna una expresión al nombremsg
; luego imprime el mensaje; luego devuelve el nombre del usuario como resultado de todo el bloque. Comoname
es aString
, esto significa quehello
es un procedimiento que devuelve aString
, por lo que tiene tipoIO String
. Y ahora puede ejecutar este procedimiento en otro lugar, tal como se ejecutagetLine
.Pfff, mónadas. ¿Quién los necesita?
fuente
Son esenciales. Sin ellos, es demasiado fácil usar Haskell como otro lenguaje imperativo, y aunque Simon Peyton Jones una vez llamó a Haskell, "el mejor lenguaje de programación imperativo del mundo", todavía te estás perdiendo la mejor parte.
Mónadas, transformadas de mónada, monoides, functores, fundores aplicativos, functores contravalentes, flechas, categorías, etc., son patrones de diseño. Una vez que se ajusta lo específico que está haciendo en uno de ellos, puede recurrir a una gran cantidad de funciones que se escriben de manera abstracta. Estas clases de tipos no están simplemente ahí para confundirte, están ahí para hacerte la vida más fácil y para hacerte más productivo. Son, en mi opinión, la razón más importante para la concisión del buen código Haskell.
fuente
¿Es estrictamente necesario si solo quieres que algo funcione? No.
¿Es necesario si quieres escribir hermosos programas idiomáticos de Haskell? Absolutamente.
Al programar en Haskell, ayuda tener dos cosas en mente:
El sistema de tipos está ahí para ayudarlo. Si compone su programa a partir de un código altamente polimórfico de clase de tipo pesado, muy a menudo puede entrar en la maravillosa situación en la que el tipo de función que desea lo guiará a la implementación correcta (¡una sola!) .
Las diversas bibliotecas disponibles se desarrollaron utilizando el principio n. ° 1.
Entonces, cuando escribo un programa en Haskell, empiezo escribiendo los tipos. Luego empiezo de nuevo y escribo algunos tipos más generales (y si pueden ser instancias de clases de tipos existentes, mucho mejor). Luego escribo algunas funciones que me dan valores de los tipos que quiero. (Luego juego con ellos
ghci
y tal vez escribo algunas pruebas de comprobación rápida). Y finalmente lo pego todomain
.Es posible escribir un feo Haskell que solo hace que algo funcione. Pero hay mucho más que aprender una vez que haya alcanzado esa etapa.
¡Buena suerte!
fuente