综观($)
和flip
类型:
ghci> :t ($)
($) :: (a -> b) -> a -> b
ghci> :t flip
flip :: (a -> b -> c) -> b -> a -> c
能否请你解释一下如何flip ($)
有这样的签名?
ghci> :t flip ($)
flip ($) :: b -> (b -> c) -> c
综观($)
和flip
类型:
ghci> :t ($)
($) :: (a -> b) -> a -> b
ghci> :t flip
flip :: (a -> b -> c) -> b -> a -> c
能否请你解释一下如何flip ($)
有这样的签名?
ghci> :t flip ($)
flip ($) :: b -> (b -> c) -> c
漂亮其实很简单:
($) :: (a -> b) -> a -> b
|______| | |
| | |
flip :: (a -> b -> c) -> b -> a -> c
因此,我们基本上是统一(a -> b -> c)
与(a -> b) -> a -> b
。为了清楚起见,让我们重命名(a -> b -> c)
到(r -> s -> t)
:
($) :: (a -> b) -> a -> b
|______| | |
| | |
flip :: (r -> s -> t) -> s -> r -> t
因此:
r
与(a -> b)
结合。s
与a
相一致。t
与b
统一。因此:
flip ($) :: s -> r -> t
:: a -> (a -> b) -> b
这相当于:
flip ($) :: b -> (b -> c) -> c
这一切就是这么简单。
编辑:
flip
函数有一个参数,(a -> b -> c)
,以及一个返回值b -> a -> c
。flip ($)
时,($)
函数将成为flip
函数的第一个参数。($)
的类型签名是unified,其参数的类型签名为flip
。(a -> b -> c)
和(a -> b) -> a -> b
。(a -> b -> c)
首先重命名为(r -> s -> t)
然后r
代替(a -> b)
,s
代替a
和t
代替b
。统一term1
和term2
当http://www.learnprolognow.org/lpnpage.php?pagetype=html&pageid=lpn-htmlse5 总结的统一算法,:
例如,我们可以写一个Prolog程序,统一条款:
% fun(R, fun(S, T)) is equivalent to (r -> s -> t).
% fun(fun(A, B), fun(A, B)) is equivalent to (a -> b) -> a -> b.
?- fun(R, fun(S, T)) = fun(fun(A, B), fun(A, B)).
R = fun(A, B),
S = A,
T = B.
一个统一的算法可以在这里找到:
term1
和term2
是常数,然后它们统一,当且仅当它们是相同的常数。例如Int
只与常数Int
相一致。它不与统一的Char
统一。term1
是变量并且term2
是非变量,则term1
被实例化为term2
(即term1 := term2
)。例如,a
和Int
与a := Int
一致。term2
是变量并且term1
是非变量,则term2
被实例化为term1
(即term2 := term1
)。例如,Int
和b
与b := Int
统一。term1
和term2
都是变量,那么它们都被实例化为彼此并且它们被称为共享值(即term1 := term2
和term2 := term1
)。例如,当统一时,a
和b
成为相同的变量。term1
和term2
是复杂的术语(例如term1
是Either a Int
和term2
是Either Char b
),然后他们统一当且仅当:
Maybe Int
和Maybe Char
具有相同的类型构造函数。但是,Maybe Int
和[Int]
不。Either a Int
和Either Char b
中,参数与a := Char
和b := Int
一致。Either a a
与Either Char Int
时,我们首先有a := Char
,然后a := Int
。这是不相容的。因此这两个术语不统一。Unflipped $
在其右侧得到a
类型的值,并且它适用于从它的左侧的功能(a -> b)
:func $ value
相同func value
。
翻转$
需要b
类型的值在其左侧和从其右侧施加函数(b->c)
它:value `flip ($)` func
相同func value
。
行了相应类型的组件:
($) :: (a -> b) -> a -> b
flip :: (a -> b -> c) -> b -> a -> c
重要的规则:在任何类型的签名,可以更换另一类型的变量每次发生不以相同的签名出现在任何地方,和得到一个完全等同于原始的类型。 (类型不关心类型变量的名称,只有哪些变量相同,哪些变量在同一个签名中是不同的。)因此,我们可以在flip
(a := x
,b := y
,c := z
)中稍微重命名类型变量,并且它是相同的类型:
($) :: (a -> b) -> a -> b
flip :: (x -> y -> z) -> y -> x -> z
稍有不同的规则:在任何类型的签名,我们可以替换类型变量的所有实例的任何类型并获得专门版本类型。让我们专门将flip
设置为与($)
兼容的类型。我们通过更换x := (a -> b)
,y := a
和z := b
:
($) :: (a -> b) -> a -> b
flip :: ((a -> b) -> a -> b) -> a -> (a -> b) -> b
现在的flip
这个特殊版本的第一个参数的类型($)
类型相匹配,我们可以计算出的flip ($)
类型:
flip ($) :: a -> (a -> b) -> b
现在我们更换b := c
,并在此之后,a := b
:
flip ($) :: b -> (b -> c) -> c
你是怎么知道(a - > b)被应用于flip的第一个参数的? – 2014-11-25 02:35:05
我编辑了我的答案。 'flip'的第一个也是唯一的参数是'(a - > b - > c)'。这并不是说'(a - > b)'适用于'a'。更像是(a - > b - > c)与'(a - > b) - > a - > b'统一。因此'a'与'(a - > b)'统一。 – 2014-11-25 02:48:38
伟大的图表。不清楚给算法提供多大帮助,但我想它不会受到伤害。 – luqui 2014-11-25 06:23:11