多元函数的点自由组合

Point free composition of multivariate functions

比如说,我们想引入不同参数的函数求和的概念(我们称它为<+>),它的行为类似于:(f1 <+> f2)(x1, x2) == f1(x1) + f2(x2).

虽然这可以很容易地手动写出,但借助笛卡尔函数积的概念使用无点样式是有意义的。后者在下面定义,对我来说似乎还不错而且很笼统:

x :: (x1 -> y1) -> (x2 -> y2) -> (x1 -> x2 -> (y1, y2))
x f1 f2 = \x1 x2 -> (f1(x1), f2(x2))

那么我们可以这样写:

(<+>):: Num a => (a -> a) -> (a -> a) -> (a -> a -> a)
(<+>) = (uncurry (+)) . x

上面的代码对我来说也很好,但 GHC 不这么认为:

 * Couldn't match type: (x20 -> y20) -> a -> x20 -> (a, y20)
                     with: ((a -> a) -> a -> a -> a, (a -> a) -> a -> a -> a)
      Expected: (a -> a)
                -> ((a -> a) -> a -> a -> a, (a -> a) -> a -> a -> a)
        Actual: (a -> a) -> (x20 -> y20) -> a -> x20 -> (a, y20)
    * Probable cause: `x' is applied to too few arguments
      In the second argument of `(.)', namely `x'
      In the expression: (uncurry (+)) . x
      In an equation for `<+>': (<+>) = (uncurry (+)) . x
    * Relevant bindings include
        (<+>) :: (a -> a) -> (a -> a) -> a -> a -> a

感觉编译器无法推断出第二个函数的类型,但为什么呢?我该怎么办,这甚至可以吗?

.运算符仅用于组合具有单个参数的函数,但是函数x有四个参数,因此您必须使用四次.

(<+>) = (((uncurry (+) .) .) .) . x

请记住,在实际代码中这不是好的风格。

如果您提供两个参数,您将看到哪里出了问题。

(<+>) = uncurry (+) . x
(<+>) a = (uncurry (+) . x) a
        = uncurry (+) (x a)
(<+>) a b = uncurry (+) (x a) b

糟糕! b 作为第三个参数传递给 uncurry,而不是像您预期的那样将 x 作为第二个参数传递。第三个和第四个参数也应该转到 x 而不是 uncurry,如:

(<+>) a b c d = uncurry (+) (x a b c d)

这是对四参数组合进行无点化的正确方法。

\a b c d -> f (g a b c d)
    = \a b c d -> (f . g a b c) d
    = \a b c -> f . g a b c
    = \a b c -> ((.) f . g a b) c
    = \a b -> (.) f . g a b
    = \a b -> ((.) ((.) f) . g a) b
    = \a -> (.) ((.) f) . g a
    = \a -> ((.) ((.) ((.) f)) . g) a
    = (.) ((.) ((.) f)) . g

然后大多数人使用节语法将其写为 (((f .) .) .) . g。将这个新事实应用到您的案例中:

\a b c d -> uncurry (+) (x a b c d)
    = (((uncurry (+) .) .) .) . x

定义

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

现在,compose2 (+) 是你的 <+>:

> :t compose2 (+)
compose2 (+) :: Num t => (a -> t) -> (d -> t) -> a -> d -> t

如您所见,它的类型比您想象的要通用一些。

compose2 already exists.