Как сгладить IO [[String]]? - PullRequest
       13

Как сгладить IO [[String]]?

5 голосов
/ 24 декабря 2011

Я новичок в 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)

Это работает с [[String]] в GHCi, как и ожидалось:

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

, но не с 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

Думаю, я не могу использовать функцию, которая должна быть чистой с типами ввода-вывода?Могу ли я конвертировать IO [[String]] в [[String]]?Как правильно решить эту проблему?

Ответы [ 2 ]

15 голосов
/ 24 декабря 2011

Вы должны понять, что означает IO something. Это не something, это действие , которое вернет something (в данном случае something - это [[String]]). Таким образом, вы ничего не можете сделать с вещью, которую возвращает действие, пока не выполните действие, которое возвращает эту вещь.

У вас есть два варианта решения вашей проблемы.

  1. Выполните действие и используйте результат. Это делается так:

    do
      ds <- domains       -- Perform action, and save result in ds
      return $ flatten ds -- Flatten the result ds
    
  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
    

PS. Возможно, вы захотите переименовать переменную domains в getDomains, чтобы уточнить, что она делает. Это не чистая ценность; это монадическое действие, которое возвращает чистое значение.

10 голосов
/ 24 декабря 2011

Вы не можете получить ничего "из" ввода / вывода, поэтому вам нужно поднять flatten, чтобы работать внутри него. Самый простой способ сделать это - fmap - точно так же, как map применяет функцию к списку, fmap применяет функцию к любому экземпляру Functor, например IO.

flattenIO xs = fmap flatten xs

В более общих случаях вы можете использовать нотацию do, чтобы получить результат в вычислениях IO. Например:

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

... в данном случае это просто окольный способ записи fmap.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...