将高阶函数提升为monad

时间:2013-03-03 12:33:26

标签: haskell functional-programming monads

假设我有一个更高阶的函数,它使用从函数参数中检索的值执行一些计算。

f :: a -> (b -> c) -> d

其中a,b,c,d是一些具体类型。

然后我有一个这种类型的函数

g :: b -> m c

其中m是一些monad。

现在有办法将g与f一起使用。那就是把f变成一个产生m d而不是d的函数,可以用g作为它的第二个参数吗?

一个具体的例子是m是IO monad,f是计算从其功能参数中检索到的n个数字之和的函数,g从标准输入中读取数字。

f n g = sum $ map g (1..n)
g :: Int -> IO Int
g _ = readLn

我知道有一些函数可以将标准输入转换为惰性列表,这可以解决这个问题,但我的实际情况并不那么简单。

假设我有一个在图表上做某事的算法。该算法使用功能参数来确定节点的邻居。这样我就可以有多个图形实现。

现在我想将此算法与非确定性图形(List monad)或未完全已知的图形(可能是monad)一起使用。我知道我可以重写算法使用monads然后使用身份monad作为基本情况,但这是唯一的方法吗?我认为在没有monad的情况下编写算法会更容易。

这种行为可能吗?我找不到它不应该的原因,但我找不到如何做的方法。

1 个答案:

答案 0 :(得分:7)

所以你想要例如派生mapM给出map。那就是你有一个简单的map

map  :: (a -> b) -> [a] -> [b]

并且您希望将其用作map on monadic structures

mapM :: Monad m => (a -> m b) -> [a] -> m [b]

我们可以通过映射IO操作,然后对它们进行排序,从mapM计算map,因此:

mapM f xs = sequence (map f xs)

现在我们有了更通用的表单,我们可以通过在the Identity monad.中运行map然后在mapM中使用map来获取mapM身份monad。

> let g :: Int -> Identity Int
      g a = return (a^2)

其中:

> runIdentity $ mapM g [1..10]
[1,4,9,16,25,36,49,64,81,100]

所以是的,你需要将你的高阶函数归为正确的级别 - 无论是monad,functor还是applicative,那么你可以自由地用其他的计算概念来代替,包括身份。

你可以通过转换函数的AST来机械地将任何纯函数重写为它的monadic:

  • 将结果值包装在return
  • 识别新的monadic子表达式并绑定它们。
  • 用monadic bind替换变量绑定;或者,如果它是适用的:
  • 用monad中的应用程序替换应用程序(小心)

E.g。

map f []     = []
map f (x:xs) = f x : map f xs

To(applicative style)

mapM f []     = return []
mapM f (x:xs) = (:) <$> f x <*> mapM' f xs

或更不清楚,并修正评估顺序:

mapM f []     = return []
mapM f (x:xs) = do
    v  <- f x
    vs <- mapM f xs
    return (v:vs)

我们可以使用地图的应用程序,因为不需要monadic绑定来将结果从一步传递到下一步。 foldl

并非如此
foldl        :: (a -> b -> a) -> a -> [b] -> a
foldl f z0 xs0 = lgo z0 xs0
     where
        lgo z []     =  z
        lgo z (x:xs) = lgo (f z x) xs

foldlM :: Monad m => (a -> b -> m a) -> a -> [b] -> m a
foldlM f z0 xs0 = lgo z0 xs0
     where
        lgo z []     = return z
        lgo z (x:xs) = do
            v <- f z x
            lgo v xs