我有一个签名为String -> String -> IO (Maybe String) 的函数现在,我使用此函数为字典构建值,最终得到:[(String,IO (Maybe String))]
这部分不适合我。如何只输入 2 个字符串,并最终得到 [(String, IO (Maybe String))] 类型的有意义的结果?
缺乏细节,假设你的情况是这样的:
f :: String -> String -> IO (Maybe String)
magic :: String -> (String, String)
magic 获取一些密钥,并以某种方式将密钥拆分为f 所需的两个输入字符串。所以,假设你有一个键列表,你会得到一个像这样的[(String, IO (Maybe String))]:
-- ks :: [String]
myMap = map (\k -> let (a,b) = magic k in (k, f a b)) ks
但是...如果我们有一个 [(String, Maybe String)] 代替会更好吗?假设我们在一个 IO 动作中......
someIOAction = do
...
myMap <- monadicMagicks (\k -> let (a,b) = magic k in (k, f a b)) ks
但是我们可以用什么monadicMagicks 来解决这个问题?让我们看看我们期望从这些表达式中得到的类型
monadicMagicks (\k -> ...) ks :: IO [(String, Maybe String)]
(\k -> let (a,b) = magic k in (k, f a b)) :: String -> (String, IO (Maybe String))
ks :: [String]
-- therefore
monadicMagicks :: (String -> (String, IO (Maybe String)))
-> [String]
-> IO [(String, Maybe String)]
停止...胡歌时间。让我们先概括一下我们正在寻找的东西。我们这里有两种基本数据类型:String,似乎是“输入”,Maybe String,似乎是“输出”。因此,让我们将String 替换为a,将Maybe String 替换为b。所以我们正在寻找(a -> (a, IO b)) -> [a] -> IO [(a, b)]。人力资源管理系统。未找到结果。好吧,如果我们能得到一个只有IO [b] 的结果类型,那么在一个do 块中我们可以把它从IO 中取出,然后zip 它和原来的键列表一起使用。这也意味着我们输入的函数不必执行 key 与 result 的配对。因此,让我们简化我们正在寻找的内容并重试:(a -> IO b) -> [a] -> IO [b]。嘿,看看吧! mapM 完美匹配该类型签名。
monadicMagicks :: (a -> IO b) -> [a] -> IO [(a, b)]
monadicMagicks f xs = do
ys <- mapM f xs
return $ zip xs ys
这实际上可以写得更简洁,并使用更通用的类型签名:
monadicMagicks :: Monad m => (a -> m b) -> [a] -> m [(a, b)]
monadicMagicks f xs = zip xs `liftM` mapM f xs
someIOAction = do
...
-- Like we said, the lambda no longer has to pair up the key with the result
myMap <- monadicMagicks (\k -> let (a,b) = magic k in f a b) ks
如果您对函数组合感到满意,最后一行也可以改写一点:
myMap <- monadicMagicks (uncurry f . magic) ks
请注意,monadicMagicks 对于某些 monad 可能没有多大意义,这可能是它不在标准库中的原因。 (例如,在列表 monad 中使用 mapM 通常意味着结果的长度与输入的长度不同):
ghci> mapM (\x -> [x, -x]) [1,2]
[[1,2],[1,-2],[-1,2],[-1,-2]]