我觉得我没有真正了解函数应用程序是如何工作的,或者我可能在关注一些我不应该关注的事情。
让我们尝试确定(fmap .)
的类型签名fmap :: (x -> y) -> f x -> f y
个(.) :: (b -> c) -> (a -> b) -> a -> c -- signatures taken from GHC
将fmap
应用到(.)
,我们基本上想使(x -> y) -> f x -> f y
和(b -> c)
相等。当我第一次尝试这样做的时候,我记得我有一段非常困难的时间。你必须将一组4个变量与一组2个变量相匹配,所以显然你必须进行一些分组,但是怎么做呢?我看到有3种可能的解决方案,它们都有效吗?还是只有1?我读到编译器没有真正考虑括号,所以我们最终得到了x -> y -> f x -> f y
和b -> c
,还有与右边关联的函数,所以fmap
实际上应该看起来像x -> (y -> (f x -> f y))
,这就导致了b = x
个c = y -> f x -> f y
从而产生(a -> x) -> (a -> y -> f x -> f y)
作为(fmap .)
的签名。
我看到的解决这个问题的分组是(a -> b) -> (f a -> f b)
b = x -> y
个c = f x -> f y
导致(a -> x -> y) -> a -> f x -> f y
,如果重命名一些类型变量,它或多或少与GHC的(a1 -> a2 -> b) -> a1 -> f a2 -> f b
相同。
现在要比较2个结果,删除括号:
x1米20英寸a -> x -> y -> a -> f x -> f y
我们可以清楚地看到,第3个和第4个参数类型被交换了,这意味着它们显然是不一样的,因为flip
是一个东西。b = x
个c = y -> f x -> f y
个
是无效的。还是我遗漏了什么?
我有一种感觉,我试图让函数基于签名工作,而我应该以相反的方式工作。:t fmap 'c'
在Couldn't match expected type 'a -> b' with actual type 'Char'
上失败,这意味着你不能真正部分地应用fmap
的a -> b
部分。你只能部分地应用fmap
本身,我猜它的第一个参数是不可分的。我不知道我是否'不过,我已经知道这背后的规律了。
我真的应该试着写一个类型化的lambda演算。
1条答案
按热度按时间3okqufwl1#
我看到了3种可能的解决方案,它们都有效吗?
不,不,不
还是只有1?
只有一个。
(x -> y) -> f x -> f y
等于(x -> y) -> (f x -> f y)
,而且只等于(x -> y) -> (f x -> f y)
,因为->
是右结合的。在其他点上加括号是错误的。我读到编译器并没有真正考虑括号,
这是错误的。编译器隐式地工作,因为括号总是以右关联的方式添加:类型
a -> b -> c -> d
只作为a -> (b -> (c -> d))
处理。相反,类型(a -> b) -> c -> d
只作为(a -> b) -> (c -> d)
处理,类型(a -> b -> c) -> d
只作为(a -> (b -> c)) -> d
处理。所以我们得到
x -> y -> f x -> f y
和b -> c
,不,那是错误的,你必须保留括号。我们必须满足类型相等
唯一的解决方法是
正如你后来发现的
为了理解统一运算中发生了什么,我认为以右结合的方式添加隐式括号是有益的。如果这样做,你可以忘记像
a -> b -> c
这样的类型,而只关心基本情况(T -> U)
和它的统一步骤: