ML类型推断的指数成本的简洁示例


11

引起我注意的是,像OCaml这样的功能语言中类型推断的成本可能很高。该主张是有一系列表达式,使得对于每个表达式,相应类型的长度在表达式的长度上是指数的。

我设计了以下顺序。我的问题是:您是否知道一个具有更简洁表达式的序列,可以实现相同的类型?

# fun a -> a;;
- : 'a -> 'a = <fun>
# fun b a -> b a;;
- : ('a -> 'b) -> 'a -> 'b = <fun>
# fun c b a -> c b (b a);;
- : (('a -> 'b) -> 'b -> 'c) -> ('a -> 'b) -> 'a -> 'c = <fun>
# fun d c b a -> d c b (c b (b a));;
- : ((('a -> 'b) -> 'b -> 'c) -> ('a -> 'b) -> 'c -> 'd) ->
   (('a -> 'b) -> 'b -> 'c) -> ('a -> 'b) -> 'a -> 'd
= <fun>
# fun e d c b a -> e d c b (d c b (c b (b a)));;
- : (((('a -> 'b) -> 'b -> 'c) -> ('a -> 'b) -> 'c -> 'd) ->
    (('a -> 'b) -> 'b -> 'c) -> ('a -> 'b) -> 'd -> 'e) ->
   ((('a -> 'b) -> 'b -> 'c) -> ('a -> 'b) -> 'c -> 'd) ->
   (('a -> 'b) -> 'b -> 'c) -> ('a -> 'b) -> 'a -> 'e
= <fun>
# fun f e d c b a -> f e d c b (e d c b (d c b (c b (b a))));;
- : ((((('a -> 'b) -> 'b -> 'c) -> ('a -> 'b) -> 'c -> 'd) ->
     (('a -> 'b) -> 'b -> 'c) -> ('a -> 'b) -> 'd -> 'e) ->
    ((('a -> 'b) -> 'b -> 'c) -> ('a -> 'b) -> 'c -> 'd) ->
    (('a -> 'b) -> 'b -> 'c) -> ('a -> 'b) -> 'e -> 'f) ->
   (((('a -> 'b) -> 'b -> 'c) -> ('a -> 'b) -> 'c -> 'd) ->
    (('a -> 'b) -> 'b -> 'c) -> ('a -> 'b) -> 'd -> 'e) ->
   ((('a -> 'b) -> 'b -> 'c) -> ('a -> 'b) -> 'c -> 'd) ->
   (('a -> 'b) -> 'b -> 'c) -> ('a -> 'b) -> 'a -> 'f
= <fun>

Answers:


14

在这个答案中,我将坚持该语言的核心ML片段,仅let遵循lambda微积分和遵循Hindley-Milner的多态。完整的OCaml语言具有其他功能,例如行多态性(如果我没记错的话,它不会改变理论上的复杂性,但是实际程序往往具有更大的类型)和模块系统(如果打了个硬仗,可能就不行了)。 -在涉及抽象特征的病理情况下终止)。

决定核心ML程序是否具有类型的最坏情况下的时间复杂度是程序大小的简单指数。此结果的经典参考是[KTU90]和[M90]。[S95]中给出了更基本但较不完整的处理。

实际上,核心ML程序的类型的最大大小实际上是程序大小的两倍。如果类型检查器必须打印程序的类型,则时间可能是双倍的;通过为树的重复部分定义缩写,可以将其恢复为简单的指数。这可以对应于在实现中共享类型树的各个部分。

您的示例显示了该类型的指数增长。但是请注意,可以通过对类型的重复部分使用缩写来给出类型的线性大小表示。这可以对应于在实现中共享类型树的各个部分。例如:

# fun d c b a -> d c b (c b (b a));;
t2 -> t2
where t2 = (t1 -> 'b -> 'c) -> t1 -> 'a -> 'd
where t1 = 'a -> 'b

这是一个从概念上讲更简单的示例:对的类型的大小是类型(x,x)的大小的两倍x,因此,如果对pair函数进行次组合,则得到的大小为。NΘ(2N)

# let pair x f = f x x;;
# let pairN x = pair (pair (pair … (pair x)…));;
'a -> tN
where tN = (tN-1 -> tN-1 -> 'bN) -> 'bN
…
where t2 = (t1 -> t1 -> 'b2) -> 'b2
where t1 = ('a -> 'a -> 'b1) -> 'b1

通过引入嵌套的多态let定义,类型的大小再次以指数形式增加。这次,没有任何共享可以消除指数级增长。

# let pair x f = f x x;;
# let f1 x = pair x in
  let f2 x = f1 (f1 x) in
  let f3 x = f2 (f2 x) in
  fun z -> f3 (fun x -> x) z;;

参考文献

[KTU90] Kfoury,J .;蒂琳 Urzyczyn,P.(1990)。“ ML可打字性是dexptime-complete”。计算机科学讲义。CAAP '90 431:206-220。[ Springer ] [ Google ]

[M90] Mairson,Harry G.(1990)。“确定ML可输入性已完成确定性的指数时间”。关于编程语言原理的第17届ACM SIGPLAN-SIGACT研讨会论文集。POPL '90(ACM):382–401。[ ACM ]

[P04]本杰明·皮尔斯。类型和编程语言的高级主题。麻省理工学院出版社,2004年。[ Amazon ]

[PR04]FrançoisPottier和DidierRémy。“ ML类型推断的本质”。[P04]中的第10章。[ pdf ]

[S95] Michael I. Schwartzbach。多态类型推断。金砖四国LS-95-3,1995年6月。ps


那么从根本上讲,类型表达式的“组合”性质与类型推断是问题的根源吗?
didierc

1
@didierc我不明白您的评论。很多事情都是组成性的。在某种程度上,根本原因是从复制对象(约束两种类型相同)和配对(->运算符)的基本操作中,可以进行指数增长(斐波纳契树)。
吉尔斯(Gilles)'所以

是的,我想这就是我的意思:类型代数从定义上讲是组合的(您在答案中使用了“组成对函数”一词,很可能是我拿到的单词),从这个意义上说,类型表达式是从较小的表达式和运算符,并且每个新的表达式组成都会将表达式大小至少缩放2倍(多态类型更复杂-三元或更多,该因子更大)。
didierc
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.