[英]How to understand the function “(.)(.)” in Haskell
我是Haskell的初学者,我遇到函数(.)(.)
,我使用:t
来获取GHCi中的类型:
:t (.)(.)
(.)(.) :: (a -> b -> c) -> a -> (a1 -> b) -> a1 -> c
如何理解类型(.)(.) :: (a -> b -> c) -> a -> (a1 -> b) -> a1 -> c
这里? 我很困扰。
函数(.)
具有类型(b -> c) -> (a -> b) -> (a -> c)
,即给定两个函数,一个从a
到b
,一个从b
到c
,它坚持它们一起形成单个a
到c
功能。
让我们再次写出(.)
的类型,但使用不同的字母来区分它们: (y -> z) -> (x -> y) -> (x -> z)
。 假设abc
版本是(.)
中的第一个(.)(.)
,而xyz
版本是第二个。 我们将第二个作为第一个参数传递给第一个参数。 请记住,第一个参数的第一个参数有类型(b -> c)
,所以我们需要将它与第二个函数的类型相匹配。
您可能会注意到这里存在不匹配: (b -> c)
是一个接受一个参数的函数,但(.)
需要两个参数。 但是在Haskell中,所有函数都是curry,这意味着一个带两个参数的函数与一个带有一个参数并返回另一个带有一个参数的函数(原始二个中的第二个)的函数实际上是相同的,并且只有那个函数然后返回真实结果。
换句话说,箭头类型构造函数从右到左绑定,我们可以放入括号使其更清晰:为了我们的目的,第二个(.)
类型更好地写为(y -> z) -> ((x -> y) -> (x -> z))
。 将其与(b -> c)
匹配,很明显这意味着b = (y -> z)
和c = ((x -> y) -> (x -> z))
。
给出的第一个参数,结果是第一个(.)
类型的其余部分,其中类型变量被我们的替换替换 - 因此(.)(.)
的类型是(a -> (y -> z)) -> (a -> ((x -> y) -> (x -> z)))
。
现在我们可以删除箭头右边的所有括号来简化这个表达式并得到(a -> y -> z) -> a -> (x -> y) -> x -> z
。 很容易看出GHCi给你的确切(模数重命名)。
这种类型和函数意味着,“给定一个二进制函数b
,它取一个a
和一个y
并返回一个z
,并给出一个类型a
a的值va
,并给出一个带有x
并返回y
的一元函数u
,最后给出一个x
类型的值vx
,给我从计算b va (u vx)
得到的z
。
你可能不需要它。 功能很有趣的唯一原因是它看起来像胸部。
这是该组合物操作的局部应用组合物操作者本身。 一般来说,我们知道如果我们将(.)
应用于某个函数f :: x -> y
,那么
>>> :t (.) f
(.) f :: (a -> x) -> a -> y
因为类型如何排列:
(b -> c) -> (a -> b) -> a -> c
x -> y
--------------------------------
(a -> x) -> a -> y
我们删除第一个参数,并用给定参数的相应类型替换剩余的b
和c
出现。
这里, f
再次只是(.)
,意味着我们识别x ~ (b -> c)
和y ~ (a -> b) -> a -> c
。 再次排列类型
(a -> x ) -> a -> y
b -> c (a -> b) -> a -> c
由于a
在顶部和底部出现时,我们需要选择一个新的变量名称为a
底部; GHC选择了a1
:
(a -> x ) -> a -> y
b -> c (a1 -> b) -> a1 -> c
将两者放在一起会产生您在GHCi中看到的类型。
(a -> b -> c) -> a -> (a1 -> b) -> a1 -> c
除了解剖学笑话,什么是 (.)(.)
?
假设你有一个函数f :: a -> b
,但是你想要一个函数g :: a -> c
,也就是说,你想要f
但具有不同的返回类型。 你唯一能做的就是找一个辅助函数h :: b -> c
,它将为你转换返回值。 你的函数g
就是h
和f
的组合:
g = h . f
但是,您可能有一个更通用的函数h' :: t -> b -> c
,它可以多种方式将b
类型的值转换为c
类型的值,具体取决于某些参数x :: t
的值。 然后根据该参数可以获得许多不同的g
s。
g = (h' x) . f
现在,给定h'
, x
和f
,我们可以返回我们的g
,所以让我们编写一个函数来执行该操作:将f
的返回值从类型b
的值“提升”到类型c
的值的函数,给定函数h'
和某个值x
:
promote h' x f = (h' x) . f
您可以将任何功能机械转换为无点形式; 我不熟悉细节,但使用PointFree.io生成
promote = ((.) .)
这只是部分应用程序(.) (.)
写成一个部分,即:
((.) (.)) h' x f == (h' x) . f
因此,我们的“胸部”操作员只是一个广义的预组合算子。
┌──────────────────────────────────────────────────────────────────────────┐
│ │
┌─────────────────────────────────────────────────┐ │
│ │ │ │
┌─────────────────────────┐ ┌──────────────────────┐ │
│ │ │ │ │ │ │ │
↓ ↓ ↓ │ ↓ │ │ │
(a -> b -> c) -> a -> (a1 -> b) -> a1 -> c
─────────── ─── ─────── ──
↓ ↓ ↓ ↓
(f) (x) (g) (y)
↓ ↓ ↓ ↓
a function a thing that works a function of one a thing that
of two arguments as the first argument argument that works as the
that returns of f returns a thing argument of g
the same type suitable as the second
(.)(.) returns argument of f
现在我们如何将这四件事结合起来?
首先,我们可以将f
应用于x
。 这给了我们什么? 一个参数的函数。 它的类型应该是b->c
,因为我们刚才应用类型的功能a->b->c
到类型的参数a
。
然后我们可以取第二个g
并将其应用于y
。 这给了我们类型b
东西。
然后我们可以采取类型的函数b->c
在第一步骤中计算,并将其应用到类型的那个东西b
在第二步骤中计算。 这给了我们类型c
东西,整个(.)(.)
构造的结果类型,这正是我们需要的。
注意通过查看类型可以发现所有这些。 无需知道该函数最初是如何实现的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.