Trataré de comenzar con un ejemplo simplificado. Digamos que esto es lo que queremos hacer:
- Abra un archivo que contiene una lista de enteros y devuélvalo.
- Ordenar esta lista
- también vamos a revertir la lista
- imprimir el resultado en la pantalla
También vamos a decir que tenemos estas funciones que podemos utilizar:
getContent :: IO [Int]
sort :: [Int] -> [Int]
reverse :: [Int] -> [Int]
show :: a -> String
putStrLn :: String -> IO()
Sólo
así que estamos claros, tendré una palabra sobre estas funciones:
getContent
: inventé esta función, pero si existiera tal función que sería su firma (puede usar getContent = return [3,7,2,1]
para realizar pruebas). Estoy seguro de que ya has visto esa firma antes y al menos entiendo vagamente que, dado que hace IO, su firma no puede ser solo getContent :: [Int]
.
sort
: Es una función definida en el módulo Data.List, el uso es simple: sort [3,1,2]
vuelve [1,2,3]
reverse
: también definido en el módulo Data.List: reverse [1,3,2]
vuelve [2,3,1]
show
: no necesita importar nada , simplemente úselo: show 11
devuelve la cadena "11"
; show [1,2,3]
devuelve la cadena "[1,2,3]"
, etc.
- putStrLn: toma una cadena, lo pone en la pantalla y vuelve IO(), ahora de nuevo, ya que hace IO su firma no puede ser sólo
putStrLn :: Stiring ->()
.
Bien, ahora tenemos todo lo que necesitamos para crear nuestro programa, el problema ahora es conectar estas funciones juntas. Vamos a empezar con las funciones de conexión:
getContent :: IO [Int]
con sort :: [Int] -> [Int]
Creo que si se consigue esta parte, obtendrá fácilmente el resto también.Entonces, el problema es que dado que getContent
devuelve IO [Int]
y no solo [Int]
, no puede simplemente ignorar o deshacerse de la parte IO
y meterla en sort
. Es decir, esto es lo que no puede hacer para conectar estas funciones:
sort (getRidOfIO getContent)
Aquí es donde la operación >>= :: m a -> (a -> m b) -> m b
viene al rescate. Ahora note que m
, a
y b
son variables de tipo así que si sustituimos m
para IO
, a
para [Int]
y b
para [Int]
, obtenemos el signagure:
>>= :: IO [Int] -> ([Int] -> IO [Int]) -> IO [Int]
Tener un vistazo de nuevo a los getContent
y sort
funciones y sus firmas y tratar de pensar cómo encajarán en el >>=
. Estoy seguro de que notará que puede usar getContent
directamente como el primer argumento para >>=
. Hasta ahora lo que hará >>=
es tomar el [Int]
en getContent
y lo coloca en la función provista como segundo argumento. ¿Pero cuál será la función en el segundo argumento? No podemos utilizar el sort :: [Int] -> [Int]
directamente, la siguiente mejor cosa que podemos intentar es
\listOfInts -> sort listOfInts
pero que todavía tiene la firma [Int] -> [Int]
por lo que no ayuda mucho. Aquí es donde el otro héroe llega a la obra, el
return :: a -> m a
.
Una vez más, a
y m
son variables de tipo, permite sustituir ellos y obtendremos
return :: [Int] -> IO [Int]
por lo que añadir \listOfInts -> sort listOfInts
y return
vamos a buscar juntos:
\listOfInts -> return $ sort listOfInts :: [Int] -> IO [Int]
Lo cual es exactamente lo que queremos poner como segundo argumento al >>=
. Por lo tanto permite finaly conectar getContent
y sort
utilizando nuestro pegamento juntos:
getContent >>= (\listOfInts -> return $ sort listOfInts)
que es lo mismo que (usando la notación do
):
do listOfInts <- getContent
return $ sort listOfInts
No, ese es el final de la mayor parte parte aterradora. Y ahora viene posiblemente uno de los momentos aha, trate de pensar cuál es el tipo de resultado de la conexión que acabamos de inventar. Lo estropearé para usted, ... el tipo de
getContent >>= (\listOfInts -> return $ sort listOfInts)
es IO [Int]
nuevamente.
Permite resumir: tomamos algo del tipo IO [Int]
y algo de tipo [Int] -> [Int]
, pegados esas dos cosas juntas y conseguimos algo nuevo tipo de IO [Int]
!
Ahora seguir adelante y tratar exactamente lo mismo: Tomar el objeto IO [Int]
que acabamos de crear y la cola juntos (usando >>=
y return
) con reverse :: [Int] -> [Int]
.
Creo que escribí demasiado, pero avíseme si algo no estaba claro o si necesita ayuda con el resto.
Wha que he descrito hasta ahora puede ser algo como esto:
getContent :: IO [Int]
getContent = return [5,2,1,7]
main :: IO()
main = do
listOfInts <- getContent
return $ sort listOfInts
return() -- This is only to sattisfy the signature of main
enlace obligatorio: http://www.haskell.org/tutorial/ por si acaso esto se pasa por alto - "suave" es relativo, por supuesto. –
Estoy aprendiendo un haskell para bien también, solo unos pocos días. – Orbit
Además de tener que usar la notación 'do' o soltar la asignación (' <-') y pasar a usar bind ('>> ='), no puede nombrar algo 'data' ya que es una palabra reservada. –