Haskell 将高阶函数提升到monad中
假设我有一个高阶函数,它使用从函数参数中检索的值执行一些计算Haskell 将高阶函数提升到monad中,haskell,functional-programming,monads,Haskell,Functional Programming,Monads,假设我有一个高阶函数,它使用从函数参数中检索的值执行一些计算 f :: a -> (b -> c) -> d 其中a、b、c、d是一些混凝土类型 然后我有一个这样的函数 g :: b -> m c 其中m是一些单子 现在有没有一种方法可以把g和f一起使用。也就是说,把f变成一个函数,它产生md而不是d,并且可以使用g作为它的第二个参数 一个具体的例子是,m是IO单子,f是一个函数,计算从其函数参数检索到的n个数字的和,g从标准输入读取一个数字 f n g = sum
f :: a -> (b -> c) -> d
其中a、b、c、d是一些混凝土类型
然后我有一个这样的函数
g :: b -> m c
其中m是一些单子
现在有没有一种方法可以把g和f一起使用。也就是说,把f变成一个函数,它产生md
而不是d
,并且可以使用g作为它的第二个参数
一个具体的例子是,m是IO单子,f是一个函数,计算从其函数参数检索到的n个数字的和,g从标准输入读取一个数字
f n g = sum $ map g (1..n)
g :: Int -> IO Int
g _ = readLn
我知道有一些函数可以将标准输入转换为惰性列表,这可以解决这个问题,但我的实际情况并没有那么简单
假设我有一个在图上做某事的算法。该算法使用函数参数来确定节点的邻居。这样我就可以有多个图形的实现
现在我想用这个算法处理一个不确定的图(List monad)或者一个不完全已知的图(可能是monad)。我知道我可以重写算法使用monad,然后在基本情况下使用identity monad,但这是唯一的方法吗?我认为在没有单子的情况下编写算法会更容易
这种行为可能吗?我找不到不应该这样做的原因,但我找不到一种方法。因此,您希望例如派生
mapM
给定map
。也就是说,您有一个简单的地图
:
map :: (a -> b) -> [a] -> [b]
你想把它当作一个
我们可以通过映射IO操作,然后对其排序,从map
计算mapM
,因此:
mapM f xs = sequence (map f xs)
现在我们有了更一般的形式,我们可以通过在中运行mapM
返回map
,然后在标识单子中的经典map
是mapM
> 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]
所以,是的,你需要将你的高阶函数泛化到正确的层次——不管是单子,还是函子,还是应用函数,然后你可以自由地替换其他的计算概念,包括恒等式
通过转换函数的AST,可以机械地将任何纯函数重写为其一元函数:
- 将结果值包装在
返回中
- 识别新的一元子表达式,并绑定它们
- 用一元结合取代可变结合;或者,如果适用:
- 用monad中的应用程序替换应用程序(注意)
要从map创建mapM,我们需要知道map的作用。这是必需的吗?也就是说,如果您示例中的map函数是其他函数(可能具有我们未知的定义),那么我们是否能够做到这一点?从你的回答来看,我想不是。你有什么解释吗?因为给定任何函数,我认为可以用这种方式重写它。那么,为什么一般来说不可能这样做呢?不管怎样,谢谢你的回答,我会等一会儿,如果没有其他人想出什么,我会把它标记为正确的。@MartinKolinek为你添加了一些派生词。谢谢,所以我想修改AST是唯一的方法,这意味着必须知道函数的定义。如果你想变得一般,要让这种情况发生在这种非常普遍的类型的所有
f
,那么这是不可能的。例如,一个可能的f
是f=()
,但由于它只是忽略了高阶函数部分,因此它将违反您正在寻找的行为。不过,函数的定义不是必需的。Don的示例map
是一个很好的选择,因为我们可以确保它具有正确的类型,尽管实际上不知道map
是如何实现的。在所有情况下,(f::(a->b)->[a]->[b])。(g::Monad m=>[MA]->m[a]:::(a->MB)->[a]->m[b]
。
> runIdentity $ mapM g [1..10]
[1,4,9,16,25,36,49,64,81,100]
map f [] = []
map f (x:xs) = f x : map f xs
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)
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