类型(c→d)→(a→b→c)→(a→b→d)的Haskell函数组合算子

jam*_*her 39 haskell pointfree function-composition

普通的功能组成属于这种类型

(.) :: (b -> c) -> (a -> b) -> a -> c
Run Code Online (Sandbox Code Playgroud)

我认为这应该概括为类型:

(.) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
Run Code Online (Sandbox Code Playgroud)

一个具体的例子:计算差分平方.我们可以写diffsq a b = (a - b) ^ 2,但感觉我应该能够编写(-)(^2)编写类似的东西diffsq = (^2) . (-).

当然,我不能.我可以做的一件事是使用一个元组而不是两个参数(-),通过转换它uncurry,但这不一样.

有可能做我想要的吗?如果没有,我误解的是什么让我认为它应该是可能的?


注意:这里已经有效地提出这个问题,但是没有给出答案(我怀疑必须存在).

Edw*_*ETT 46

我首选的实现是

fmap . fmap :: (Functor f, Functor f1) => (a -> b) -> f (f1 a) -> f (f1 b)
Run Code Online (Sandbox Code Playgroud)

如果只是因为它很容易记住.

将f和f1实例化为和时(->) c,(->) d分别得到类型

(a -> b) -> (c -> d -> a) -> c -> d -> b
Run Code Online (Sandbox Code Playgroud)

这是哪种类型

(.) . (.) ::  (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c
Run Code Online (Sandbox Code Playgroud)

但它更容易摆脱fmap . fmap版本,它推广到其他仿函数.

有时这是写的fmap fmap fmap,但写的,因为fmap . fmap它可以更容易扩展,以允许更多的参数.

fmap . fmap . fmap 
:: (Functor f, Functor g, Functor h) => (a -> b) -> f (g (h a)) -> f (g (h b))

fmap . fmap . fmap . fmap 
:: (Functor f, Functor g, Functor h, Functor i) => (a -> b) -> f (g (h (i a))) -> f (g (h (i b))
Run Code Online (Sandbox Code Playgroud)

等等

一般fmap用自己组成n次可以用到fmap n级深!

由于函数形成了一个Functor,这为n个参数提供了管道.

有关更多信息,请参阅Conal Elliott的语义编辑器组合器.

  • 这个答案很有启发性.我不知道`(.)`只是`fmap`专门用于函子`( - >)c`. (6认同)
  • 此外,您可以混合使用其他语义组合器,例如`first`和`second`.例如:`fmap.first.fmap.second ::(Functor g,Functor f)=>(b - > c) - > f(g(d1,b),d) - > f(g(d1,c) ),d)`.这些组合可以非常容易地编写和读取,因为它们提供了通过编辑的整体值的类型到实际被修改的子值的路径. (4认同)

ham*_*mar 30

误解是你将类型a -> b -> c的函数看作具有返回类型的两个参数的函数c,而实际上它是一个带有返回类型的参数的函数,b -> c因为函数类型与右边相关(即它与...相同)a -> (b -> c).使得无法使用标准函数组合运算符.

要了解原因,请尝试将(.)类型为(y -> z) -> (x -> y) -> (x -> z)operator的运算符应用于两个函数,g :: c -> d并且f :: a -> (b -> c).这意味着我们必须y与之相c结合b -> c.这没有多大意义.怎样才能yc和函数返回c?那将是一个无限类型.所以这不起作用.

仅仅因为我们不能使用标准组合运算符,它不会阻止我们定义自己的运算符.

 compose2 :: (c -> d) -> (a -> b -> c) -> a -> b -> d
 compose2 g f x y = g (f x y)

 diffsq = (^2) `compose2` (-)
Run Code Online (Sandbox Code Playgroud)

通常情况下,最好避免在这种情况下使用无点样式

 diffsq a b = (a-b)^2
Run Code Online (Sandbox Code Playgroud)

  • 你可以在没有定义自己的构图的情况下"到达那里":`sumSq a =(^ 2).(+ A)`.通过命名和应用第一个参数,我们构造了一个类型为"b - > c"的函数,用于常规组合. (5认同)
  • 我喜欢无点风格`(g.).f`,但我承认它看起来有点令人困惑的部分`((^ 2).).( - )` (4认同)
  • 确实你可以,但它很快变得混乱,在这种情况下它"感觉不对",因为它在功能是可交换的时候以不同的方式对待这两个参数. (3认同)

mig*_*yte 19

我不知道执行此操作的标准库函数,但实现它的无点模式是组合函数:

(.) . (.) :: (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c
Run Code Online (Sandbox Code Playgroud)

  • 虽然这是一个可爱的技巧,但我不建议编写那种代码. (5认同)
  • 这让我的大脑受伤了,而且很棒. (3认同)
  • @hammar真的.我也不会.但它确实对`(.)`如何与只有一个参数的函数进行交互提供了不同的见解. (2认同)
  • 撰写与撰写。有趣。 (2认同)

Dan*_*ton 8

我打算在评论中写这个,但它有点长,它来自强大的和哈马尔.

我建议我们围绕运营商进行标准化,例如.*for compose2.**for compose3.使用mightybyte的定义:

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

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

diffsq :: (Num a) => a -> a -> a
diffsq = (^2) .* (-)

modminus :: (Integral a) => a -> a -> a -> a
modminus n = (`mod` n) .* (-)

diffsqmod :: (Integral a) => a -> a -> a -> a
diffsqmod = (^2) .** modminus
Run Code Online (Sandbox Code Playgroud)

是的,modminus并且diffsqmod是非常随机且毫无价值的功能,但它们很快并且显示了重点.注意通过在另一个组合函数中组合来定义下一级别是多么容易(类似于fmapEdward提到的链接).

(.***) = (.) . (.**)
Run Code Online (Sandbox Code Playgroud)

实际上,compose12从上到下,编写函数名称而不是运算符的时间更短

f .*********** g
f `compose12` g
Run Code Online (Sandbox Code Playgroud)

虽然计算星号很累,但我们可能想要在4或5时停止惯例.


[编辑]另一个随机的想法,我们可以使用.:compose2,.:.对于compose3,.::对于compose4,.::.对于compose5,.:::对于compose6,让点数(在初始一个之后)可视地标记要向下钻取的参数数量.我想我更喜欢明星了.


gde*_*ohn 6

正如Max评论中指出的那样:

diffsq = ((^ 2) .) . (-)
Run Code Online (Sandbox Code Playgroud)

您可以f . g将其视为对 应用一个参数g,然后将结果传递给f(f .) . g将两个参数应用于g,然后将结果传递给f((f .) .) . g对 应用三个参数g,依此类推。

\f g -> (f .) . g :: (c -> d) -> (a -> b -> c) -> a -> b -> d
Run Code Online (Sandbox Code Playgroud)

如果我们用一些函数f :: c -> df左侧的部分应用程序)对组合运算符进行左截,我们得到:

(f .) :: (b -> c) -> b -> d
Run Code Online (Sandbox Code Playgroud)

所以我们有这个新函数,它期望一个函数来自b -> c,但我们的ga -> b -> c,或者等效地,a -> (b -> c)。我们需要先申请一个,a然后才能得到我们需要的东西。好吧,让我们再次迭代:

((f .) .) :: (a -> b -> c) -> a -> b -> d
Run Code Online (Sandbox Code Playgroud)