是否存在用于类型签名的已知组合函数(a - > b) - > (b - > c) - > (a - >(b,c))?

时间:2017-03-11 15:48:31

标签: haskell function-composition

我已经在一个算法中工作了一段时间,该算法需要编写它的函数部分,以便合成的返回值包含两个函数的输出。例如(在JavaScript中):

const double = x => x * 2
const increment = x => x + 1

const doubleThenIncrement = composeConservingOutputs(increment, double)

doubleThenIncrement(2) // => [4, 5]

我一直想知道这是否是一种已知的构图模式。在Haskell中,这似乎是以一种干净的方式实现的,可以将一个箭头的输出连接到另一个箭头的扇出和id:

import Control.Arrow 

double = (*) 2
increment = (+) 1

doubleThenIncrement = arr double >>> (arr id &&& arr increment)

但我无法找到这种构图是否是一种常见/已知的模式。

我发现这很有用,因为它允许非常容易地检查中间函数的输出以进行调试,这让我觉得这可能已经是一件事了。

Hoogle search for the pattern (a -> b) -> (b -> c) -> (a -> (b, c))证明无效。

由于

2 个答案:

答案 0 :(得分:2)

如果没有其他导入,您可以使用

doubleThenIncrement = fmap increment . (id >>= (,)) . double

此函数首先将其参数加倍,然后使用函数的Monad实例将double的返回值用作 {em}两个参数的(,)函数,然后将increment应用于结果元组中元素的 second

只需一次导入,您就可以简化中间部分:

import Control.Monad
doubleThenIncrement = fmap increment . join (,) . double

(对于函数,join的实现实际上是join f = \x -> f x x。我们使用(,)的结果为double调用join (,) . double\x -> (,) (double x) (double x)是{ {1}}是(double x, double x)。)

您可以定义一个以incrementdouble作为参数的函数来返回doubleThenIncrement

> let composePreservingOutputs f g = fmap f . join (,) . g
> composePreservingOutputs increment double $ 2
(4,5)

在所有可怕的无点荣耀中,它将是

-- Courtesy of pointfree.io
composePreservingOutputs = (. (join (,) .)) . (.) . fmap

(或者,你可以写

composePreservingOutputs f g x = (f x, g (f x))

但那里的乐趣在哪里?)

答案 1 :(得分:1)

在Haskell中提出你想要的通用解决方案是很棘手的,因为中间值需要是相同的类型。

注销中间值的模式是Writer monad适合的模式。这里的限制是“写入”值必须实现Monoid类型类。如果您只是将其用于调试,我怀疑您的情况可以正常,即您可以将值转换为String,这是Monoid

如果你喜欢箭头,我会定义一个函数writeParam f x = (show x, f x)你可以包装你的所有函数。返回元组Monoid a => ((,) a)的函数实际上是一个简单的编写器monad实现,所以你可以用这样的常规monad组合来组合这些函数:

doubleThenIncrement :: (Num n, Show n) => n -> (String, n)
doubleThenIncrement = writeParam double >=> writeParam increment

顺便说一下,如果你喜欢箭头,你也可以将writeParam定义为writeParam = (show &&&),或者如果你想更通用的话,还可以定义writeParam = (id &&&)。虽然IMO不太清楚。