2011-12-24 21 views
5

Soy nuevo en Haskell y tengo algunas dificultades para entender algunos de sus conceptos.Cómo aplanar IO [[String]]?

Mientras jugaba con IO, quería aplanar un IO [[String]].

Un ejemplo de lo que he intentado:

module DatabaseTestSO where 

import Database.HDBC 
import Database.HDBC.MySQL 
import Data.Foldable 

convSqlValue :: [SqlValue] -> [String] 
convSqlValue xs = [ getString x | x <- xs ] 
    where getString value = case fromSql value of 
       Just x -> x 
       Nothing -> "Null" 

listValues :: [[SqlValue]] -> [[String]] 
listValues [] = [] 
listValues xs = [ convSqlValue x | x <- xs ] 

flatten :: [[a]] -> [a] 
flatten = Data.Foldable.foldl (++) [] 

domains :: IO [[String]] 
domains = 
    do conn <- connectMySQL defaultMySQLConnectInfo { 
       mysqlHost  = "hostname", 
       mysqlDatabase = "dbname", 
       mysqlUser  = "username", 
       mysqlPassword = "pass" } 

     queryDomains <- quickQuery conn "SELECT name FROM domains" [] 

     return (listValues queryDomains) 

que trabaja con [[String]] en GHCi como se esperaba:

*DatabaseTestSO> flatten [["blah","blab","wah"],["bloh","blob","woh"],["blih","blib","wuh"]] 
["blah","blab","wah","bloh","blob","woh","blih","blib","wuh"] 

pero no lo hace con IO [[String]] donde consigo

*DatabaseTestSO> flatten domains 

<interactive>:1:9: 
    Couldn't match expected type `[[a0]]' 
       with actual type `IO [[String]]' 
    In the first argument of `flatten', namely `domains' 
    In the expression: flatten domains 
    In an equation for `it': it = flatten domains 

¿Supongo que no puedo usar una función que se supone que es pura con tipos IO? ¿Puedo convertir IO [[String]] en [[String]]? ¿Cómo resuelvo este problema correctamente?

+4

'flatten' se llama' concat' y se define en 'Preludio'. –

+0

Ya veo. ¿'Prelude' se importa automáticamente cuando no se usa GHCi? –

+0

Sí (relleno ...) –

Respuesta

15

Tienes que darte cuenta de lo que significa IO something. No es un something, es una acción que devolverá un something (en este caso, something es [[String]]). Entonces, no puedes hacer nada con lo que la acción devuelve, hasta que realizas la acción, lo que devuelve esa cosa.

Tiene dos opciones para resolver su problema.

  1. Realice la acción y utilice el resultado. Esto se hace así:

    do 
        ds <- domains  -- Perform action, and save result in ds 
        return $ flatten ds -- Flatten the result ds 
    
  2. crear una nueva acción que toma el resultado de algún tipo de acción, y se aplica una función a la misma. La nueva acción luego devuelve el valor transformado. Esto se hace con la función liftM en el módulo Control.Monad.

    import Control.Monad 
    -- ... 
    
    do 
        -- Creates a new action that flattens the result of domains 
        let getFlattenedDomains = liftM flatten domains 
    
        -- Perform the new action to get ds, which contains the flattened result 
        ds <- getFlattenedDomains 
    
        return ds 
    

PS. Es posible que desee cambiar el nombre de su variable domains a getDomains para aclarar lo que hace. No es un valor puro; es una acción monádica que devuelve un valor puro.

+0

Su explicación fue muy útil. –

+3

Nota: 'fmap' =' liftM' = 'liftA' =' <$> '. –

10

No puede obtener nada "fuera" de IO, por lo que lo que necesita hacer es levantar flatten para trabajar dentro de él. La forma más sencilla de hacerlo es fmap, al igual que map aplica una función sobre una lista, fmap aplica una función sobre cualquier instancia de Functor, como IO.

flattenIO xs = fmap flatten xs 

En los casos más generales, puede utilizar la notación do para conseguir las cosas de IO cálculos. Por ejemplo:

flattenIO xs = do ys <- xs 
        return (flatten ys) 

... que es sólo una manera indirecta de la escritura fmap en este caso.

+0

Gracias por su respuesta. Tendré que aprender mucho. –