haskell 是否存在类型签名(a -> B)->(b -> c)->(a ->(b,c))的已知复合函数?

jqjz2hbq  于 2023-08-06  发布在  其他
关注(0)|答案(2)|浏览(130)

我已经在一个算法中工作了一段时间,该算法需要组合它的函数部分,以便组合的返回值包含两个函数的输出。例如(在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))证明是徒劳的。
谢啦,谢啦

wooyq4lh

wooyq4lh1#

在不需要额外导入的情况下,您可以使用

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

字符串
此函数首先将其参数加倍,然后使用函数的Monad示例将double的返回值用作(,)函数的 both 参数,然后将increment应用于结果元组中的 second 元素。
通过一个导入,您可以稍微简化中间部分:

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


(For函数,join的实现实际上是join f = \x -> f x x。我们使用double的结果调用(,)作为两个参数。join (,) . double\x -> (,) (double x) (double x)(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


(Or,你可以只写

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


但那有什么乐趣呢)

2izufjch

2izufjch2#

在Haskell中为您想要的内容提出通用解决方案是很棘手的,因为中间值需要具有相同的类型。
注销中间值的模式是Writer monad所适合的。这里的限制是“写入”值必须实现Monoid类型类。我怀疑这在你的情况下是可以的,如果你只是用它来调试的话。您可以将值转换为String,即Monoid
我会定义一个函数writeParam f x = (show x, f x)(如果你喜欢箭头),你可以把所有的函数都 Package 进去。返回元组Monoid a => ((,) a)的函数实际上是writer monad的一个简单实现,所以你可以用常规的monad组合来组合这些函数,如下所示:

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

字符串
顺便说一下,如果你喜欢箭头,你也可以将writeParam定义为writeParam = (show &&&),如果你想更通用的话,甚至可以只定义为writeParam = (id &&&)。虽然IMO对此不太清楚。

相关问题