我正在学习Haskell,并编写一个简短的解析脚本作为练习。我的大部分脚本都包含纯函数,但我有两个嵌套的IO组件:
我有什么作品,但是嵌套的IO和fmap的图层"感觉"笨重,就像我应该避免嵌套IO(不知何故),或者更巧妙地使用do notation来避免所有的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
我最终使用listDirectory作为readFile的输入来处理IO [IO String]
。不是难以管理,但是如果我使用do notation打开[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嵌套,这会有多么复杂。
建议?
提前致谢。
答案 0 :(得分:8)
我认为你想要main
main = do
paths <- getPaths "./configs"
flatConfigs <- traverse getConfig paths
let blockConfigs = fmap chunk flatConfigs
-- Parse and do stuff with config data.
return ()
比较
fmap :: Functor f => (a -> b) -> f a -> f b
和
traverse :: (Applicative f, Traversable t) => (a -> f b) -> t a -> f (t b)
它们非常相似,但traverse
允许您使用IO
等效果。
这些类型再次专门用于比较:
fmap :: (a -> b) -> [a] -> [b]
traverse :: (a -> IO b) -> [a] -> IO [b]
(traverse
也称为mapM
)
答案 1 :(得分:5)
你对“嵌套”的想法&#39;实际上是一个非常好的洞察monad是什么。可以将Monads视为具有两个附加操作的Functors,返回类型为const start_time = "2017-03-02T15:57:00Z";
const stop_time = "2017-03-02T17:51:00Z";
const local_timestamp = 1488498242256; // Thu Mar 02 2017 16:44:02 GMT-0700 (MST)
const start = moment(start_time).valueOf(); // 1488470220000
const stop = moment(stop_time).valueOf(); // 1488477060000
const is_between = local_timestamp >= start && local_timestamp <= stop; // false
并加入类型const start = moment(start_time).utc().valueOf(); // 1488470220000
const stop = moment(stop_time).utc().valueOf(); // 1488477060000
const is_between = local_timestamp >= start && local_timestamp <= stop; // false
。然后我们可以创建类型为const now = moment(local_timestamp);
const isBetween = now.isBetween(start, stop); // false
的函数:
a -> m a
所以我们想在这里使用join,但目前只有m (m a) -> m a
,所以我们的monad组合器不会直接帮助。让我们使用hoogle搜索a -> m b
,我们的第一个结果看起来很有希望。它是fmap :: (a -> m b) -> m a -> m (m b)
f =<< v = join (fmap f v) :: (a -> m b) -> m a -> m b
如果我们查看相关函数,我们也会找到类似m [m a]
的{{1}}。
有了这些知识,我们可以写一下:
m [m a] -> m (m [a])