【问题标题】:Does "Value Restriction" practically mean that there is no higher order functional programming?“价值限制”实际上是否意味着没有更高阶的函数式编程?
【发布时间】:2010-04-15 12:09:40
【问题描述】:

“值限制”实际上是否意味着没有高阶函数式编程?

我有一个问题,每次我尝试做一些 HOP 时,都会遇到 VR 错误。示例:

let simple (s:string)= fun rq->1 
let oops= simple ""

type 'a SimpleType= F of (int ->'a-> 'a)
let get a = F(fun req -> id)  
let oops2= get ""

我想知道这是 VR 的特定实现的问题,还是在类型系统中不包含突变的可变类型推断语言中没有解决方案的一般问题。

【问题讨论】:

  • 您介意将其改写为一个有意义的问题吗?
  • 我不是函数式编程专家,但这种限制不是其他函数式语言(例如 Haskell)不共享的类 ML 语言的特性吗?如果是这样,您可能需要相应地标记您的问题。
  • 我的问题是:可变性是否意味着推断函数语言中的严格 VR。

标签: functional-programming type-inference higher-order-functions mutability


【解决方案1】:

“值限制”是否意味着没有高阶函数式编程?

绝对不会! 值限制几乎不会干扰高阶函数式编程。它所做 所做的是将多态 函数(而非高阶函数)的某些应用限制在顶层。


让我们看看你的例子。 您的问题是oopsoops2 都是身份函数并且具有forall 'a . 'a -> 'a 类型。换句话说,每个都是多态值。但右边不是所谓的“句法值”;它是一个功能应用程序。 (函数应用程序不允许返回多态值,因为如果是这样,您可以使用会破坏类型系统的可变引用和列表构造一个 hacky 函数;也就是说,您可以编写一个终止函数类型forall 'a 'b . 'a -> 'b

幸运的是,在几乎所有实际情况下,所讨论的多态值都是一个函数,您可以通过 eta-expanding 来定义它:

let oops x = simple "" x

这个习语看起来有一些运行时成本,但取决于内联器和优化器,编译器可以摆脱它——只是可怜的类型检查器有问题。

oops2 的例子比较麻烦,因为要对值构造函数进行打包解包:

let oops2 = F(fun x -> let F f = get "" in f x)

这个比较繁琐,但是匿名函数fun x -> ...是句法值,F是数据类型构造函数,构造函数应用于句法值也是句法值,而Bob是你叔叔. F 的打包和解包都将被编译成标识函数,所以oops2 将编译成完全oops 相同的机器码。

当您希望运行时计算返回像 None[] 这样的多态值时,情况会更加糟糕。正如 Nathan Sanders 所暗示的,您可以使用像 rev [] 这样简单的表达式来违反值限制:

Standard ML of New Jersey v110.67 [built: Sun Oct 19 17:18:14 2008]
- val l = rev [];
stdIn:1.5-1.15 Warning: type vars not generalized because of
   value restriction are instantiated to dummy types (X1,X2,...)
val l = [] : ?.X1 list
-  

那里没有更高阶的东西!然而价值限制仍然适用。

在实践中值限制对高阶函数的定义和使用没有障碍;你只是 eta-expand。

【讨论】:

  • 虽然您所说的 VR 总体上是对的,但您并没有回答 OP 的问题。首先,如果您阅读其他答案或自己尝试了该程序,eta-expanding oops2 将不起作用。其次,我认为OP已经理解了这个限制背后的原因,他想知道是否有更好的解决方案。
  • @Wei 那是因为我完全不理解 OP 的问题(现在仍然不理解)。但我试图澄清。
  • get是一元函数,所以不能写let oops2 x = get "" x
  • @Wei:感谢您发现我的错误。我已经修复了oops2 示例并立即对其进行了测试。
  • "在实践中,值限制对高阶函数的定义和使用没有任何障碍;你只是 eta-expand" 对不起,这句话末尾使用了 "just"这里为时过早。每次我想做一些对类型有意义的事情时,我都必须找到一个破坏我的 api 流畅性的 hack。我很抱歉,但我不能问我的 api 用户:请减少 eta 并解压缩你的值。其他人能看出这是没有意义的吗?
【解决方案2】:

我不知道值限制的细节,所以我搜索了一下,发现this article。以下是相关部分:

显然,我们不会在程序中编写表达式 rev [],所以它不是多态的并不特别重要。但是如果我们使用函数调用创建一个函数呢?使用柯里化函数,我们一直这样做:

- val revlists = map rev;

这里的 revlists 应该是多态的,但是值限制把我们搞砸了:

- val revlists = map rev;
stdIn:32.1-32.23 Warning: type vars not generalized because of
   value restriction are instantiated to dummy types (X1,X2,...)
val revlists = fn : ?.X1 list list -> ?.X1 list list

幸运的是,我们可以使用一个简单的技巧来使 revlist 具有多态性。我们可以将 revlists 的定义替换为

- val revlists = (fn xs => map rev xs);
val revlists = fn : 'a list list -> 'a list list

现在一切正常,因为 (fn xs => map rev xs) 是一个语法值。 (等效地,我们可以使用更常见的有趣语法:

- fun revlists xs = map rev xs;
val revlists = fn : 'a list list -> 'a list list

结果相同。)在文献中,将函数值表达式 e 替换为 (fn x => e x) 的技巧称为 eta 扩展。经验表明,eta 展开通常足以处理值限制。

总而言之,高阶编程不像无点编程那样受到限制。这可能解释了我在将 Haskell 代码转换为 F# 时遇到的一些麻烦。


编辑:具体来说,这是修复第一个示例的方法:

let simple (s:string)= fun rq->1 
let oops= (fun x -> simple "" x)     (* eta-expand oops *)

type 'a SimpleType= F of (int ->'a-> 'a)
let get a = F(fun req -> id)
let oops2= get ""

我还没有弄清楚第二个,因为类型构造函数妨碍了。

【讨论】:

  • 但是我的第二个例子不是无点的,只是一个类型中的简单函数。而且我不能做 eta 减少(无论如何我觉得它很难看):s
  • (fun req -> id) 缺少一些参数:(fun req x -> id x) 是 eta 扩展版本。不过,这两种乐趣仍然是高阶函数。 (但第二个并没有解决值限制,至少在 OCaml 中。)
  • 是的,它不能解决 VR 问题。所以它不是关于无点,而是关于返回函数的函数。
【解决方案3】:

这里是 F# 上下文中的the answer to this question。 总而言之,在 F# 中,将类型参数传递给泛型(=多态)函数是运行时操作,因此泛化实际上是类型安全的(例如,您不会在运行时崩溃)。然而,如此概括的价值的行为可能令人惊讶。

对于 F# 中的这个特定示例,可以使用类型注释和显式类型参数恢复泛化:

type 'a SimpleType= F of (int ->'a-> 'a)
let get a = F(fun req -> id)  
let oops2<'T> : 'T SimpleType = get ""

【讨论】:

    猜你喜欢
    • 2018-10-27
    • 2015-11-17
    • 1970-01-01
    • 2020-11-30
    • 1970-01-01
    • 2019-06-02
    • 1970-01-01
    • 2017-06-03
    • 2019-12-11
    相关资源
    最近更新 更多