2010-11-16 9 views
5

Ok, estoy tratando de entender a IO en Haskell, y pensé que escribiría una pequeña aplicación corta que tratara con páginas web para hacerlo. El fragmento Me voy a tropezar en IS (con perdón de bobince, aunque para ser justos, no estoy tratando de análisis sintáctico HTML aquí, sólo extraer uno o dos valores):Ayuda de Haskell Curl

titleFromUrl url = do 
    (_, page) <- curlGetString url [CurlTimeout 60] 
    matchRegex (mkRegexWithOpts "<title>(.*?)</title>" False True) page 

la anterior debe tomar una URL en forma de cadena, escanear la página a la que apunta con matchRegex y devolver Nothing o Just [a], donde a es la cadena coincidente (posiblemente multilínea). Lo frustrante es que cuando intento hacer

Prelude> (_, page) <- curlGetString url [CurlTimeout 60] 
Prelude> matchRegex (mkRegexWithOpts "<title>(.*?)</title>" False True) page 

en el intérprete, hace exactamente lo que yo quiero. Cuando trato de cargar la misma expresión, y el imports asociado de un archivo, me da un error de inferencia de tipo que lo indica couldn't match expected type 'IO b' against inferred type 'Maybe [String]'. Esto me dice que me estoy perdiendo algo pequeño y fundamental, pero no puedo entender qué. He intentado convertir explícitamente page en una cadena, pero eso es solo programación por superstición (y no funcionó en ningún caso).

¿Alguna pista?

Respuesta

8

Sí, GHCi acepta cualquier tipo de valor. Se puede decir:

ghci> 4 
4 
ghci> print 4 
4 

Pero esos dos valores (4 y print 4) no son claramente iguales. Lo que está haciendo Magic GHC es que si lo que escribiste se evalúa como IO something, entonces ejecuta esa acción (e imprime el resultado si something no es ()). Si no lo hace, llama al show en el valor e imprime eso. De todos modos, esta magia no es accesible desde tu programa.

Cuando dicen: se espera

do foo <- bar :: IO Int 
    baz 

baz a ser de tipo IO something, y es un error de tipo contrario. Eso le permitiría ejecutar E/S y luego devolver un valor puro. Se puede comprobar que con señalar que desugaring los rendimientos anteriores:

bar >>= (\foo -> baz) 

Y

-- (specializing to IO for simplicity) 
(>>=) :: IO a -> (a -> IO b) -> IO b 

Por lo tanto

bar :: IO a 
foo :: a 
baz :: IO b 

La manera de solucionarlo es dar vuelta a su valor de retorno en un valor IO utilizando la función return:

return :: a -> IO a -- (again specialized to IO) 

Su código es entonces:

titleFromUrl url = do 
    (_, page) <- curlGetString url [CurlTimeout 60] 
    return $ matchRegex (mkRegexWithOpts "<title>(.*?)</title>" False True) page 

Para la mayor parte de la discusión anterior, se puede sustituir cualquier mónada para IO (por ejemplo. Maybe, [], ...) y seguirá siendo cierto.

+0

Funciona, pero solo como seguimiento; ¿Entiendo correctamente que esto básicamente significa que no puedo devolver una cadena regular de una función que realiza IO?que más bien necesita ser 'IO String' (o, como en el caso anterior' IO (Maybe [String]) '? ¿Qué pasa si quiero hacer algo como concatenar el valor de retorno de' titleFromUrl' con otra cadena, o imprimir sin el 'Just [~ a]' envolviéndolo? Lo siento si esta es una pregunta estúpida, soy algo nuevo en lo de la mecanografía fuerte. – Inaimathi

+0

Está bien, solo necesitas enlazar. Si tienes un valor ' m' del tipo 'IO a', luego puede escribir' do {x <- m; stuff} ', y' x' tendrá el tipo 'a', para lo cual puede hacer lo que quiera. La única restricción es que 'stuff' tiene que ser una especie de valor' IO', que puede ser una llamada de valor o función, o puede ser más enlaces '<-'. Así que puedes hacer cualquier cosa con' String' en el interior, siempre y cuando eventualmente devuelve un 'IO' algo. Sugiero leer un tutorial de mónada Hay toneladas, aquí hay dos: http://blog.sigfpe.com/2006/08/you-could-have-invented-monads-and. html o LYAH capítulo 11 y 12. – luqui

+0

[facepalm] Ok, creo ** que enlace ayudado. Me estaba equivocando al olvidar que no se puede garantizar el orden de ejecución en un lenguaje perezoso y puramente funcional. La modificación del fragmento solo le dice al compilador que fuerce el resultado de 'matchRegex' antes de usarlo en cualquier lugar. ¿Estoy cerca? – Inaimathi