The weakness of Haskell's docs
Una clásica crítica a la documentación de Haskell es que la comunidad usa los (excelentes) tipos como suplemento de documentación real, en vez de usarlos como complemento. Uno se pregunta “¿cómo se hace X?” y te responden que veas los tipos de la librería y lo “sabrás”. Lo cual puede ser cierto para ciertas mentes, aunque no mucho para la mía1.
Ejemplos de documentación que lleva al éxito se encuentran en librerías famosas de Python (requests, django) o javascript, que incluyen tutoriales. Esto hace que comenzar sea mucho más rápido. Pero el paso 2 no lo es. Una referencia es irremplazable, y ese es el propósito de los tipos de las librerías de Haskell.
Debo decir que es “exhaustante” en cierto sentido que lo primero que uno ve al entrar a un paquete haskell es una lista de 20 tipos, antes de saber siquiera para qué los va a usar. No ayuda mucho. Y después ve implementaciones y son parecidas a lo siguiente:
-- implementación ficticia de algo
doThing :: Type w e a -> Type w e b
doThing = go (bar . foo)
where go f x = case f x of
Just y -> y
Nothing -> go (f . f) x
Y se supone que hay que entender inmediatamente las 5 abstracciones que
se encuentran ahí en 3 líneas, inmediatamente. Pero a futuro, alguien opinará
que el case .. of es muy explícito y lo reemplazará por una llamada a maybe.
Por supuesto, no hay documentación para la función, porque se debería poder
entender mediante los tipos.
La función doThing está improvisada a la mala, probablemente no haga typecheck.
Pero si lo hace, estoy bastante seguro de que incluye un loop infinito.
Desafortunadamente, los loops infinitos son más o menos comunes en el mundo Haskell:
-- implementación real, en la librería estándar
fix f = let x = f x in x
Afortunadamente, esta función sí está documentada. A continuación pego la documentación:
fix fis the least fixed point of the functionf, i.e. the least definedxsuch thatf x = x.For example, we can write the factorial function using direct recursion as
>>> let fac n = if n <= 1 then 1 else n * fac (n-1) in fac 5 120This uses the fact that Haskell’s
letintroduces recursive bindings. We can rewrite this definition usingfix,>>> fix (\rec n -> if n <= 1 then 1 else n * rec (n-1)) 5 120Instead of making a recursive call, we introduce a dummy parameter
rec; when used withinfix, this parameter then refers tofix’s argument, hence the recursion is reintroduced.
Haskell con menos abstracción
Tal como la debilidad del ecosistema JS es que usan demasiadas dependencias, la debilidad del ecosistema Haskell es que usan demasiadas abstracciones. Así que, cuando hice mi memoria, decidí que yo no caería en este problema. Usaría tipos concretos, o con un parámetro como máximo (kind (* -> *)). Haría implementaciones explícitas, sin usar estilo point-free. No haría tantos tipos distintos, porque creaban confusión. Tampoco usaría muchas dependencias, solo comprometiéndome con algunas necesarias para lo que hacía2.
¿Cómo resultaron estas ideas? Algunas bien, otras no. La que más me arrepiento
es parametrizar poco mis tipos. A veces uno no nota las diferencias sutiles que hay
entre los distintos tipos involucrados en un programa, y resultaba útil agregar
información extra como parámetros nuevos, aunque en otras partes se rellenara
con () (el equivalente a void de Haskell). Y lo que se ganaba en términos
de claridad de los tipos era fácil de conseguir de vuelta definiendo sinónimos
de tipos (type X = Y Int). Así uno se da cuenta de por qué el paquete Haskell
promedio tiene tantos tipos definidos al principio, por desagradable que sea
como usuario.
Lo de no usar estilo point-free yo diría que fue buena idea eso sí.
Conclusión
He divagado bastante, pero:
- Las librerías, si tienen la intención de ser usadas, merecen un tutorialcillo en la documentación.
- Los tipos sí son buena referencia, pero para alguien que ya sabe usar la librería.
- Nada que ver, pero me carga la sintaxis de Haskell, lo difícil que hace separar implementaciones en distintas líneas, y por ello lo mucho que fomenta usar variables de una letra.
Si ya has usado Haskell, todo esto sonará obvio. Si no, quizá no? Quién sabe, lo publico igual.
Published 2023-06-06