Как эффективно работать с вложенными монадами, полученными из SQLite3 и HDBC

Я должен признать, что я все еще не "там", когда дело доходит до эффективной работы с монадами, поэтому, пожалуйста, прости меня, если это простой вопрос. Я также должен извиниться за то, что не предоставил рабочий код, поскольку эти вопросы больше связаны с концепцией, чем с реальной реализацией, над которой я сейчас работаю.

Я работаю с базой данных SQLite(3) и, конечно, хотел бы отправлять запросы и получать результаты обратно. Находясь уже в IO, fetchAllRows функция возвращает [[SqlValue]] который должен быть преобразован. Поскольку SQLite очень либерален с точки зрения текста и значений с плавающей запятой (а Haskell вообще не либерален, когда речь идет о типах), безопасное преобразование с использованием safeFromSql кажется уместным. Теперь, если вам удастся сделать все это в одной функции, вы получите

myfunc :: String -> [SqlValue] -> IO [[ Either ConvertError a]]

или что-то в этом роде, верно? Мне кажется, что работа с этими структурами вложенных монад может быть достаточно распространенной (и достаточно громоздкой), чтобы существовал стандартный способ облегчения работы с тем, о чем я не знаю?

1 ответ

Решение

Кажется, что проблема решается только некоторыми конкретными функциями, а затем наиболее четко в синтаксисе do. Приведенные ниже функции решают проблему доступа к пакету direct-sqlite3 к базе данных SQLite (а также вставляет обработчик REGEXP).

import Text.Regex.Base.RegexLike
import qualified Text.Regex.PCRE.ByteString as PCRE
import qualified Data.ByteString as BS
import Data.Text (pack,Text,append)
import Data.Text.Encoding (encodeUtf8)
import Data.Int (Int64)
import Database.SQLite3

pcreRegex :: BS.ByteString -> BS.ByteString -> IO Int64
pcreRegex reg b = do
    reC <- pcreCompile reg
    re <- case reC of
        (Right r) -> return r
        (Left (off,e) ) -> fail e
    reE <- PCRE.execute re b
    case reE of
        (Right (Just _)) -> return (1 :: Int64)
        (Right (Nothing)) -> return (0 :: Int64)
        (Left (c,e)) -> fail e -- Not a good idea maybe, but I have no use for error messages.
    where pcreCompile = PCRE.compile defaultCompOpt defaultExecOpt



sqlRegexp :: FuncContext -> FuncArgs -> IO ()
sqlRegexp ctx args = do
    r <- fmap encodeUtf8 $ funcArgText args 0
    t <- fmap encodeUtf8 $ funcArgText args 1
    res <- pcreRegex r t
    funcResultInt64 ctx res 

getRows :: Statement -> [Maybe ColumnType] -> [[SQLData]] -> IO [[SQLData]]
getRows stmt coltypes acc = do
  r <- step stmt
  case r of
    Done -> return acc
    Row -> do
      out <- typedColumns stmt coltypes
      getRows stmt coltypes (out:acc)


runQuery q args columntypes dbFile = do
    conn <- open $ pack dbFile
    createFunction conn "regexp" (Just 2) True sqlRegexp
    statement <- prepare conn q
    bind statement args
    res <- fmap reverse $ getRows statement (fmap Just columntypes) [[]]
    Database.SQLite3.finalize statement
    deleteFunction conn "regexp" (Just 2) 
    close conn
    return $ res

Надеюсь, это поможет кому-то здесь.

Другие вопросы по тегам