monad中的值,嵌套在数据结构中?

Jay*_*Jay 5 monads haskell functional-programming

假设在Haskell程序中我有一些类型如下的数据:

  • IO [ IO (Int, String, Int) ], 要么
  • IO [ (Int, String, IO Int) ], 要么
  • [ (Int, String, IO Int) ]

但我有纯粹的功能,应该操作[ (Int, String, Int) ].似乎我必须笨拙地从IO monad中删除内部值,直到我得到类似IO [(Int,string,Int)]然后(从IO monad内部)应用纯函数.我想,没有简单的预定义方法可以做到这一点?将整个数据结构提升为monad的东西,将所有内部类型转换为纯类型?(那会很方便!)

Tom*_*rst 6

您可以使用该liftM*功能从Control.Monad模块,或liftA*用于功能applicatives.

liftM 允许你解除纯函数在Monad中工作,例如:

ghci> let s = return "Hello" :: IO String
ghci> liftM reverse s
"olleH"
Run Code Online (Sandbox Code Playgroud)

这样您就不必在s >>= \x -> return (reverse x)任何地方手动编写" ".

虽然,这对你的[(String, Int, IO Int)]例子没有帮助,如果你有的纯函数处理一个[(String, Int, Int)].由于元组中的第三个元素确实不是Int.

在那种情况下,我建议先写一个函数[(String, Int, IO Int)] -> IO [(String, Int, Int)],然后应用提升的纯函数.


这是我能做到的最常用的功能:

conv :: Monad m => (f (m a) -> m (f a)) -> [f (m a)] -> m [f a]
conv f = sequence . map f
Run Code Online (Sandbox Code Playgroud)

你可以像这样调用它:

liftTrd :: Monad m => (a, b, m c) -> m (a, b, c)
liftTrd (x, y, mz) = mz >>= \z -> return (x, y, z)

conv liftTrd [("hi", 4, return 2)] :: IO [(String, Int, Int)]
Run Code Online (Sandbox Code Playgroud)

只有当你有一个类型很深的monad时,这个函数才有效.如果你有多个,我认为你应该考虑你所使用的类型,看看你是否能够简化它.