多元函数的点自由组合
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.
比如说,我们想引入不同参数的函数求和的概念(我们称它为<+>
),它的行为类似于:(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.