Haskell中的反向函数组合

时间:2013-11-23 04:04:13

标签: haskell function-composition category-theory

考虑以下Haskell代码:

countWhere :: (a -> Bool) -> [a] -> Int
countWhere predicate xs = length . filter predicate $ xs

在JavaScript中,这将写成如下:

function countWhere(predicate, xs) {
    return xs.filter(predicate).length;
}

如您所见,函数组合与JavaScript中的链接方法非常相似。我真的很喜欢链接方法从左到右读取的方式。在Haskell中,我可以使用>>>中的Control.Arrow函数和反向函数应用程序执行类似的操作,如下所示:

import Control.Arrow

($>) :: a -> (a -> b) -> b
($>) = flip ($)

countWhere :: (a -> Bool) -> [a] -> Int
countWhere predicate xs = xs $> filter predicate >>> length

现在我想用无点样式编写这个函数。使用函数组合我会写如下:

(.:) :: (b -> c) -> (d -> a -> b) -> d -> a -> c
(.:) = (.) (.) (.)

countWhere ::  (a -> Bool) -> [a] -> Int
countWhere = length .: filter

但是我想使用反向函数组合以无点样式编写此函数,如下所示:

(.:) :: (b -> c) -> (d -> a -> b) -> d -> a -> c
(.:) = (.) (.) (.)

(:.) :: (d -> a -> b) -> (b -> c) -> d -> a -> c
(:.) = flip (.:)

countWhere :: (a -> Bool) -> [a] -> Int
countWhere = filter :. length

我的抱怨是我必须根据功能组成而不是反向功能组合来定义:.功能。那就是:

(:.) = flip $ (.) (.) (.)

-- instead of

(:.) = (>>>) (>>>) (>>>)

当然(>>>) (>>>) (>>>)的类型错误。这不是我要找的功能。

功能组合物的优点在于它可以自身组成以形成如上所述的“更高阶功能组合物”。因此,虽然它的类型签名直观地向后,但它实际上是向前的,这解释了为什么f . g = \x -> f (g x)而不是f . g = \x -> g (f x)

这让我想到了我的实际问题:有没有办法用反向函数组合(即>>>)而不是flip相应的“更高”来定义“高阶反向函数组合”订单功能组合“?

我正在寻找一个源于类别理论或其他数学分支的答案。

1 个答案:

答案 0 :(得分:7)

所以这是一个伪无点答案

(.:.) :: (a -> b -> c) -> (c -> d) -> a -> b -> d
f .:. g = (,) f >>> app >>> (>>> g)

这取决于类别理论中所谓的“指数”。 Expontentials基本上提供两个函数

curry :: ((a, b) -> c, a) -> c^b
eval  :: (c^b, b)         -> c

这或多或少是curryuncurry ($)的一般版本。

这可以转换为免费(pointfree

(.:.) = (. flip (>>>)) . (>>>) . (>>> app) . (,)
(.:.) = (,) >>> fmap app >>> (>>>) >>> ((<<<) >>>)

哪个好,太可怕了。另一种选择是使用curryuncurry。 Curry和uncurry必不可少见证指数与箭之间的同构现象:Hom((a, b), c) ~~ Hom(a, c^b)。在Hask中。

(.:.) = uncurry >>> (>>>) >>> (>>>curry)