简体   繁体   English

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

[英]Is there a known composition function for the type signature (a -> b) -> (b -> c) -> (a -> (b, c))?

I've been working for a while in an algorithm that requires composing it's function parts so that the return value of the composition contains the output of both functions. 我已经在算法中工作了一段时间,需要编写它的函数部分,以便合成的返回值包含两个函数的输出。 For example (in JavaScript): 例如(在JavaScript中):

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

const doubleThenIncrement = composeConservingOutputs(increment, double)

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

I've been wondering if this is a known composition pattern. 我一直想知道这是否是一种已知的构图模式。 In Haskell , this seems to be achievable in a clean way piping the output of one arrow to the fanout of the other arrow and id: Haskell中 ,这似乎是以一种干净的方式实现的,可以将一个箭头的输出连接到另一个箭头的扇出和id:

import Control.Arrow 

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

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

but I couldn't find if this composition is a common/known pattern or not. 但我无法找到这种构图是否是一种常见/已知的模式。

I find this useful because it allows to very easily inspect the output of the intermediate functions for debugging, which made me think this is probably a thing already. 我发现这很有用,因为它允许非常容易地检查中间函数的输出以进行调试,这让我觉得这可能已经是一件事了。

A Hoogle search for the pattern (a -> b) -> (b -> c) -> (a -> (b, c)) proves unfruitful. Hoogle搜索模式(a - > b) - >(b - > c) - >(a - >(b,c))证明无效。

Thanks 谢谢

With no additional imports, you can use 没有额外的导入,您可以使用

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

This function first doubles its argument, then uses the Monad instance of functions to use return value of double as both arguments for the (,) function, then applies increment to the second of the elements in the resulting tuple. 此函数首先将其参数加倍,然后使用函数的Monad实例将double返回值用作(,)函数的两个参数,然后将increment应用于生成的元组中的第二个元素。

With one import, you can simplify the middle part a little: 只需一次导入,您就可以简化中间部分:

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

(For functions, the implementation of join is effectively join f = \\x -> fxx . We call (,) using the result of double for both arguments. join (,) . double is \\x -> (,) (double x) (double x) is (double x, double x) .) (对于函数, join的实现实际上是join f = \\x -> fxx 。我们使用两个参数的double结果调用(,)join (,) . double\\x -> (,) (double x) (double x)(double x, double x) 。)

You can define a function that takes increment and double as arguments to return doubleThenIncrement : 您可以定义一个以incrementdouble作为参数的函数来返回doubleThenIncrement

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

In all its horrific point-free glory, it would be 在其所有可怕的无点荣耀中,它将是

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

(Or, you could just write (或者,你可以写

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

but where's the fun in that?) 但那里的乐趣在哪?)

It's tricky to come up with general solution for what you want in Haskell because the intermediate values need to be of the same type. 在Haskell中提出你想要的通用解决方案是很棘手的,因为中间值需要是相同的类型。

The pattern of logging out intermediate values is something that the Writer monad is suited to. 注销中间值的模式是Writer monad适合的模式。 The restriction here is that the "written" values must implement the Monoid typeclass. 这里的限制是“写入”值必须实现Monoid类型类。 I suspect this is OK in your case if you are just using this for debugging, ie you can convert the values to String , which is a Monoid . 我怀疑如果你只是用它进行调试就可以了,即你可以将值转换为String ,这是一个Monoid

I would define a function writeParam fx = (show x, fx) if you like arrows) which you can wrap all your functions in. Functions which return tuples Monoid a => ((,) a) are actually a simple implementation of the writer monad, so you can then compose these functions with regular monad composition like so: 如果你喜欢箭头writeParam fx = (show x, fx)我会定义一个函数writeParam fx = (show x, fx)你可以包装你的所有函数。返回元组的函数Monoid a => ((,) a)实际上是一个简单的Monoid a => ((,) a)实现monad,所以你可以用常规的monad组合来组合这些函数,如下所示:

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

By the way, if you like arrows, you could also define writeParam as writeParam = (show &&&) or even just writeParam = (id &&&) if you want to be more generic. 顺便说一句,如果你喜欢箭头,你也可以将writeParam定义为writeParam = (show &&&) writeParam = (id &&&)如果你想更通用的话,甚至只需要writeParam = (id &&&) Although IMO this is less clear. 虽然IMO不太清楚。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Haskell函数组成 - (a - > b) - >(a - > c) - >(b - > c - > d) - >(a - > d) - Haskell Function Composition - (a -> b) -> (a -> c) -> (b -> c -> d) -> (a -> d) Haskell function (c→d)→(a→b→c)→(a→b→d)类型的组合运算符 - Haskell function composition operator of type (c→d) → (a→b→c) → (a→b→d) 对于Haskell类型签名,例如babel :: a-> b-> c,c是返回类型吗? - For Haskell type signature, like babel :: a -> b -> c, is c the return type? 带签名的函数的名称:`(a - > a - > b) - >(a - > b)` - Name for the function with signature: `(a -> a -> b) -> (a -> b)` 提升函数`a→b→c`到`[a]→[b]→[[c]]` - Lifting function `a → b → c` to `[a] → [b] → [[c]]` 统一c - > a - > b和(a - > b) - > c - Unifying c -> a -> b and (a -> b) -> c 这是什么类型的数学:a - > b - > c - What type of math is this: a -> b -> c 将类型级别列表'[a,b,c,...]转换为函数a-> b-> c-> - Convert type-level list '[a,b,c,…] to function a->b->c-> 此函数是否有任何用例:foo ::(b-> c)->(a-> b)->(a-> c) - Are there any use cases for this function : foo :: (b -> c) -> (a -> b) -> (a -> c) 类型签名 a -> b -> a 和 c -> a -> c 之间有区别吗? - Is there a difference between type signatures a -> b -> a and c -> a -> c?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM