Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.2k views
in Technique[技术] by (71.8m points)

haskell - How to flatten IO [[String]]?

I am new to Haskell and have some difficulties wrapping my head around some of it's concepts.

While playing around with IO I wanted to flatten an IO [[String]].

An example of what I have tried:

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)

That works with [[String]] in GHCi as expected:

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

but does not with IO [[String]] where I get

*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

I guess I can not use a function that is supposed to be pure with IO types? Can I convert IO [[String]] to [[String]]? How do I solve this problem correctly?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You have to realize what IO something means. It's not a something, it's an action that will return a something (In this case, something is [[String]]). So, you cannot do anything with the thing that the action returns, until you perform the action, which returns that thing.

You have two options to solve your problem.

  1. Perform the action, and use the result. This is done like this:

    do
      ds <- domains       -- Perform action, and save result in ds
      return $ flatten ds -- Flatten the result ds
    
  2. Create a new action that takes the result of some action, and applies a function to it. The new action then returns the transformed value. This is done with the liftM function in the Control.Monad module.

    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. You might want to rename your domains variable to getDomains to clarify what it does. It's not a pure value; it's a monadic action that returns a pure value.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...