Simplemente habría funciones específicas de IO
para encadenar acciones de IO
. Si bien en principio los tipos podrían ser diferentes a las funciones de Monad
, no puedo pensar en alternativas razonables de antemano.
Quizás el equivalente de >>=
se llamaría then
, similar a las promesas de JavaScript¹. El análogo de return
podría llamarse wrap
. La biblioteca estándar expondría estas dos funciones, junto con cualquier otra función específica de IO que sea útil:
entonces :: IO a -> (a -> IO b) -> IO b
wrap :: a -> IO a
- ¿Cómo es el lenguaje de programación Swift mejor que otros lenguajes?
- ¿Cuál es el flujo básico de mensajes en iMessage? ¿Existe un "interruptor" central y una base de datos centralizada con todos los usuarios de iPhone?
- Cómo ser un buen programador mientras estoy aprendiendo iOS usando un lenguaje rápido
- ¿Qué plataforma será más efectiva para ganar dinero usando el desarrollo de aplicaciones: Android, Windows o iOS?
- Cómo usar Spotify en iOS en India
Cuando vamos a escribir funciones usando IO
, combinamos nuestras piezas individuales con then
y lambdas, tal vez sufriendo terribles recuerdos de Node.
getLine `then` \ line -> putStr line
Casi idéntico a lo que podríamos escribir en JavaScript con la nueva sintaxis lambda:
getLine (). then (line => putStr (line))
Después de hacer esto lo suficiente, nos hartaríamos y agregaríamos algo de azúcar de sintaxis al idioma, específico de IO
, al igual que otros idiomas tienen azúcar de sintaxis para promesas (es decir, async
y await
). El azúcar de sintaxis puede parecerse mucho a la notación do (o las comprensiones de Scala, que son una versión ligeramente diferente de la notación do), pero también puede parecer expresiones let
con alguna palabra clave.
OCaml en realidad tiene una sintaxis de azúcar como esta específica para su biblioteca de promesa monádica (LWT), implementada como una macro. En OCaml, se ve así:
let% lwt s = getLine en putStr s
lo que desearía usar la función de bind
de LWT (es decir, nuestra función específica de IO):
LWT.bind getLine (diversión s -> putStr s)
Nuestro hipotético azúcar de sintaxis específica de IO en Haskell podría verse así:
let-io s = getLine en putStr s
y desugaría a
getLine `then` \ s -> putStr s
En definitiva, ¿qué hemos hecho? Hemos tomado nuestro lenguaje y agregado las funciones básicas que necesitamos para componer los valores de IO
( wrap
, then
… etc.) y algo de azúcar de sintaxis para hacerlo más aceptable. Es una repetición de la historia experimentada con promesas en todo, desde JavaScript hasta C # y OCaml.
También es probable que agreguemos muchas funciones de conveniencia específicas de IO
y tal vez incluso más azúcar de sintaxis, pero lo que describí sería el núcleo de IO
en Haskell.
Excepto por el azúcar de sintaxis, todo esto se puede hacer en el idioma existente. Solo necesitaría aprovechar las primitivas del compilador para operar con valores de IO
, es decir, la implementación de wrap
y then
dependería de la funcionalidad primitiva expuesta por el compilador y el tiempo de ejecución en lugar de Haskell puro.
Y todo lo que terminamos haciendo es reinventar mónadas pero mucho más estrechas, especializadas solo para IO
una historia repetida en muchos otros idiomas con promesas. Mirando wrap
y then
y let-io
, no es obvio que podría ser útil para muchas otras cosas: programación probabilística, programación no determinista, manejo de errores, concurrencia, continuaciones, registro, gestión de estado, programación reactiva …
Si alguien se diera cuenta de esto, ¿qué harían? Acabarían wrap
y then
en una clase:
clase Thennable t donde
wrap :: a -> ta
entonces :: ta -> (a -> tb) -> tb
y piratearían una extensión OverloadedIOLet
que vincularía el azúcar de sintaxis let-io
a la clase Thennable
lugar de específicamente a IO
.
¿Y dónde estaríamos? Tendríamos Monad
pero más feo, sin referencia a la teoría subyacente, sin leyes claras, un nombre horrible y sintaxis azucarada robada poco a poco de instalaciones específicas de IO
. Probablemente habría llegado una década más tarde que la programación monádica general de Haskell, con Haskell en el ínterin siendo como otros lenguajes con promesas, sin aprovechar la generalidad de una abstracción elegante y simple.
notas al pie
¹ Las promesas naturalmente forman una mónada, pero la API de JavaScript no refleja esto. En cambio, el método de JavaScript combinado ambos >>=
y fmap
en una función despachando dinámicamente en el tipo de resultado. Si la función a la que pasa devuelve un valor normal, se convierte en una promesa, pero si devuelve una promesa, la promesa se aplana.
Esto hace que toda la API sea menos elegante y regular, y le impide producir una promesa de una promesa, que es una limitación real para cualquier código que se supone que es completamente genérico sobre lo que funciona.