如何压扁IO [[String]]?

mat*_*ull 5 io haskell

我是Haskell的新手,并且在围绕其中的一些概念时遇到了一些困难.

在玩IO时我想要压平IO [[String]].

我尝试过的一个例子:

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)
Run Code Online (Sandbox Code Playgroud)

这符合[[String]]预期的GHCi:

*DatabaseTestSO> flatten [["blah","blab","wah"],["bloh","blob","woh"],["blih","blib","wuh"]]
["blah","blab","wah","bloh","blob","woh","blih","blib","wuh"]
Run Code Online (Sandbox Code Playgroud)

但不是IO [[String]]我得到的地方

*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
Run Code Online (Sandbox Code Playgroud)

我想我不能使用一个纯粹的IO类型的函数?我可以转换IO [[String]][[String]]?如何正确解决此问题?

dfl*_*str 15

你必须意识到什么IO something意思.它不是一个something,它是一个将返回something动作(在这种情况下,something[[String]]).因此,在执行操作返回该操作之前,您无法对操作返回的操作执行任何操作.

您有两种方法可以解决您的问题.

  1. 执行操作,并使用结果.这样做是这样的:

    do
      ds <- domains       -- Perform action, and save result in ds
      return $ flatten ds -- Flatten the result ds
    
    Run Code Online (Sandbox Code Playgroud)
  2. 创建一个采取某些操作结果的新操作,并将一个函数应用于该操作.然后新操作返回转换后的值.这是通过模块中的liftM功能完成的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
    
    Run Code Online (Sandbox Code Playgroud)

PS.您可能希望重命名domains变量getDomains以阐明它的作用.这不是一个纯粹的价值; 这是一种回归纯粹价值的一元行动.

  • 注意:`fmap` =`liftM` =`liftA` =`<$>`. (3认同)

C. *_*ann 10

你无法从IO中获得任何东西,所以你需要做的就是解除flatten它内部的工作.最简单的方法是 - fmap就像map在列表上fmap应用函数一样,在任何Functor实例上应用函数,例如IO.

flattenIO xs = fmap flatten xs
Run Code Online (Sandbox Code Playgroud)

在更一般的情况下,您可以使用do符号来IO计算计算中的内容.例如:

flattenIO xs = do ys <- xs
                  return (flatten ys)
Run Code Online (Sandbox Code Playgroud)

...... fmap在这种情况下,这只是一种迂回的写作方式.