Я изучаю Haskell и пишу короткую парсинг script как упражнение. Большинство моих script состоят из чистых функций, но у меня есть два вложенных компонента IO:
- Прочитайте список файлов с пути.
- Прочитайте содержимое каждого файла, который, в свою очередь, будет использоваться для большей части остальной части программы.
У меня есть работы, но вложенные IO и слои fmap "чувствуют" неуклюжими, например, я должен либо избегать вложенного ввода-вывода (как-то), либо более умело использовать обозначения, чтобы избежать всех fmaps. Мне интересно, если я слишком усложняю ситуацию, делаю это неправильно и т.д. Вот какой-то соответствующий код:
getPaths :: FilePath -> IO [String]
getPaths folder = do
allFiles <- listDirectory folder
let txtFiles = filter (isInfixOf ".txt") allFiles
paths = map ((folder ++ "/") ++) txtFiles
return paths
getConfig :: FilePath -> IO [String]
getConfig path = do
config <- readFile path
return $ lines config
main = do
paths = getPaths "./configs"
let flatConfigs = map getConfigs paths
blockConfigs = map (fmap chunk) flatConfigs
-- Parse and do stuff with config data.
return
В итоге я столкнулся с IO [IO String]
с помощью listDirectory в качестве входного для readFile. Не неуправляемый, но если я использую обозначение, чтобы развернуть [IO String]
для отправки какой-либо функции парсера, я все равно в конечном итоге либо использую вложенный fmap
, либо загрязняю мои якобы чистые функции с помощью IO-осведомленности (fmap и т.д.). Последнее кажется хуже, поэтому я делаю первое. Пример:
type Block = [String]
getTrunkBlocks :: [Block] -> [Block]
getTrunkBlocks = filter (liftM2 (&&) isInterface isMatchingInt)
where isMatchingInt line = isJust $ find predicate line
predicate = isInfixOf "switchport mode trunk"
main = do
paths <- getPaths "./configs"
let flatConfigs = map getConfig paths
blockConfigs = map (fmap chunk) flatConfigs
trunks = fmap (fmap getTrunkBlocks) blockConfigs
return $ "Trunk count: " ++ show (length trunks)
fmap, fmap, fmap... Я чувствую, что я непреднамеренно сделал это более сложным, чем это необходимо, и не могу представить, насколько это было бы запутанно, если бы я имел более глубокое вложение IO.
Предложения?
Спасибо заранее.