Как общее замечание: старайтесь всегда думать о том, что ваши функции делают со своими входами, предполагая, что они уже получили их. Ваши функции, похоже, связаны как с получением их входов, так и с их преобразованием. Пусть они просто преобразуются и используют некоторую внешнюю функцию, чтобы получить их входные данные.
Давайте посмотрим, сможем ли мы взять то, что вы думаете об этом, и переформулировать это по-новому:
У вас есть:
readDB :: IO [[SqlValue]]
readDB = ... -- not given here
func1 :: IO [[String]]
func1 = do
xx <- readDB
return (map (map fromSql) xx)
Теперь вы хотите написать что-то, что преобразует это. Один из способов - просто скопировать шаблон, который вы уже использовали:
funcWnt :: IO [[String]]
yy <- func1
return ... -- some transformation of yy
funcWnt2 :: IO (Splice m)
zz <- funcWnt
return ... -- some transformation of zz into a Splice m
Обратите внимание, что написанные в стиле func1
это вовсе не функции, а значения в IO
. Если эти функции не выполняют IO
, (а func1
, конечно, нет), то написание этого способа не является идиоматическим, неуклюжим, немодульным и помещает весь ваш чистый код в IO
. Вместо этого, попробуйте написать их чисто:
func1 :: [[SqlValue]] -> [[String]]
func1 = map (map fromSql)
funcWnt :: [[String]] -> [[String]]
funcWnt = ... -- some transformation
funcWnt2 :: [[String]] -> Splice m
funcWnt2 = ... -- some transformation into a Splice m
Теперь вы можете легко комбинировать их и использовать в одном IO
значении:
dbToSplice :: IO (Splice m)
dbToSplice = do
xx <- readDB
return $ (funcWnt2 . funcWnt . func1) xx
Или, может быть, более идиоматически (не бойтесь >>=
, выучите это рано):
dbToSplice :: IO (Splice m)
dbToSplice = readDB >>= return . funcWnt2 . funcWnt . func1
Теперь, если ради аргумента, funcWnt2
нужно что-то сделать в IO
. Тогда определения будут выглядеть так:
funcWnt2 :: [[String]] -> IO (Splice m)
funcWnt2 zz = do
... -- compute something with zz
return ... -- returning some Splice m
dbToSplice :: IO (Splice m)
dbToSplice = readDB >>= funcWnt2 . funcWnt . func1
Наконец, обратите внимание, что я помещаю сигнатуры типов в каждую функцию верхнего уровня. Вы должны привыкнуть делать это тоже. Он не только помогает прояснить ваши намерения (для вас самих, других разработчиков или даже для себя через неделю), но также устраняет множество неуклюжих аннотаций типа, которые вы должны были вставить в func1
.