列表理解很容易理解。请查看以下定义中的h
。它使用类型为pure_xs
的{{1}}和类型为[Int]
的{{1}},并使用列表推导中的两者。
pure_f
大。现在使用两个略有不同的表达式Int -> String
和pure_xs :: [Int]
pure_xs = [1,2,3]
pure_f :: Int -> String
pure_f a = show a
h :: [(Int,Char)]
h = [(a,b) | a <- pure_xs, b <- pure_f a]
-- h => [(4,'4'),(5,'5'),(6,'6')]
。我想使用列表推导构建monadic_f
,看起来尽可能与monadic_xs
类似。我觉得解决方案将涉及生成一系列IO操作,并使用g
在IO monad中生成类型h
的列表。
sequence
答案 0 :(得分:10)
写这个的自然方式是
do xs <- monadic_xs
ys <- mapM monadic_f xs
return (zip xs ys)
但是我们无法将其自然地转换为列表理解,因为我们需要(>>=)
绑定来提取monadic值。 Monad变形金刚将是交织这些效果的途径。我们来看看transformers
ListT
monad transformer - even though it's not actually a monad transformer。
newtype ListT m a = ListT { runListT :: m [a] }
listT_xs :: ListT IO Int
listT_xs = ListT monadic_xs
listT_f :: Int -> ListT IO String
liftT_f = ListT . fmap return . monadic_f
>>> runListT $ do { x <- listT_xs; str <- listT_f x; return (x, str) }
[(1,"1"),(2,"2"),(3,"3")]
这似乎有用,我们可以打开MonadComprehensions
以列表理解格式编写它。
>>> runListT [ (x, str) | x <- listT_xs, str <- listT_f x ]
[(1,"1"),(2,"2"),(3,"3")]
这与我能想到的纯版本的结果类似,但它有一些危险的缺陷。首先,我们使用的ListT
由于它破坏了monad变换器定律而可能不直观,其次,我们只使用列表monadic效应的一小部分---通常列表将采用笛卡尔产品,而不是拉链。
listT_g :: Int -> ListT IO String
listT_g = ListT . fmap (replicate 3) . monadic_f
>>> runListT [ (x, str) | x <- listT_xs, str <- listT_g x ]
[(1,"1"),(1,"1"),(1,"1"),(2,"2"),(2,"2"),(2,"2"),(3,"3"),(3,"3"),(3,"3")]
要解决这些问题,您可能需要尝试使用pipes
。你会在那里得到“正确的”解决方案,尽管它看起来不像列表理解那么多。