可以将自身作为参数传递的函数类型

时间:2014-07-05 16:41:34

标签: haskell

深入思考这个计算中的类型,真的很深

id id

从右到左,第一个 id的类型为

 a -> a

这意味着第一个作为参数的第二个 id必须具有类型

(a -> a) -> (a -> a)

这些类型不匹配!好的你可以说第一个 id实际上有这个后来的类型,但这会使第二个 id类型为

((a -> a) -> (a -> a)) -> ((a -> a) -> (a -> a))

我认为,尽管函数可以具有泛型或变量类型,但是在计算时绑定到某些函数的函数。在我看来,似乎每次调用都定义了 new id函数。

2 个答案:

答案 0 :(得分:8)

你是对的。 id是多态的。 每个使用都会独立实例化其类型。例如,考虑像id "Hello" ++ show (id 5)这样的哑巴。在第一次使用时,它是id :: String -> String。在第二次使用中,它是id :: Integer -> Integer(至少与多态文字的正常默认规则一致)。你通过嵌套调用id来做的事情是同样的事情 - 只是在最内层使用一些类型变量代替具体类型。

但这并不意味着实施会发生变化。 id始终是相同的功能。每次使用都会改变的唯一方法是类型检查器如何处理它。

顺便说一句,这可以用来崩溃GHC的类型检查器。您已经注意到id类型的大小在您将其应用于自身几次时会呈指数级增长。再做几次,它需要的内存比你的系统代表最外层id的类型要多。

答案 1 :(得分:3)

处理这种情况的正确技术是多态。身份功能

id x = x

有类型∀α。 α→α,其被读作“对于每种类型α,id将类型α的元素映射到类型α的元素”。为应用程序提供类型

id id

我们认为如下:

  • 第一个id的类型为∀α。 α→α,
  • 第二个id具有类型∀β。 β→β,其中我们将约束变量α重命名为β以避免混淆,
  • 如果我们将第一个id中的α设置为∀β。 β→β然后我们看到第一个id映射了∀β类型的元素。 β→β对typeβ型元素的影响。 β→β,
  • 因此整个表达式都可以,并且类型为∀β。 β→β,与∀α相同。 α→α。

没有神秘感。进一步阅读:System F