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

发布于 2024-08-29 04:16:37 字数 323 浏览 19 评论 0原文

“值限制”实际上意味着不存在高阶函数式编程吗?

我有一个问题,每次我尝试做一点 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 特定实现的问题,或者是在不包含类型系统突变的可变类型推断语言中没有解决方案的一般问题。

Does "Value Restriction" practically mean that there is no higher order functional programming?

I have a problem that each time I try to do a bit of HOP I get caught by a VR error. Example:

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 ""

and I would like to know whether it is a problem of a prticular implementation of VR or it is a general problem that has no solution in a mutable type-infered language that doesn't include mutation in the type system.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

默嘫て 2024-09-05 04:16:37

“值限制”是否意味着不存在高阶函数式编程?

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


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

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

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 扩展即可。

Does “Value Restriction” mean that there is no higher order functional programming?

Absolutely not! The value restriction barely interferes with higher-order functional programming at all. What it does do is restrict some applications of polymorphic functions—not higher-order functions—at top level.


Let's look at your example.
Your problem is that oops and oops2 are both the identity function and have type forall 'a . 'a -> 'a. In other words each is a polymorphic value. But the right-hand side is not a so-called "syntactic value"; it is a function application. (A function application is not allowed to return a polymorphic value because if it were, you could construct a hacky function using mutable references and lists that would subvert the type system; that is, you could write a terminating function type type forall 'a 'b . 'a -> 'b.

Luckily in almost all practical cases, the polymorphic value in question is a function, and you can define it by eta-expanding:

let oops x = simple "" x

This idiom looks like it has some run-time cost, but depending on the inliner and optimizer, that can be got rid of by the compiler—it's just the poor typechecker that is having trouble.

The oops2 example is more troublesome because you have to pack and unpack the value constructor:

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

This is quite a but more tedious, but the anonymous function fun x -> ... is a syntactic value, and F is a datatype constructor, and a constructor applied to a syntactic value is also a syntactic value, and Bob's your uncle. The packing and unpacking of F is all going to be compiled into the identity function, so oops2 is going to compile into exactly the same machine code as oops.

Things are even nastier when you want a run-time computation to return a polymorphic value like None or []. As hinted at by Nathan Sanders, you can run afoul of the value restriction with an expression as simple as 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
-  

Nothing higher-order there! And yet the value restriction applies.

In practice the value restriction presents no barrier to the definition and use of higher-order functions; you just eta-expand.

蓝海似她心 2024-09-05 04:16:37

我不知道值限制的详细信息,所以我搜索并找到 这篇文章。这是相关部分:

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

- val revlists = 地图 rev;

这里的修订列表应该是多态的,但值限制让我们陷入困境:

- val revlists = 地图 rev;
stdIn:32.1-32.23 警告:类型变量未泛化,因为
   值限制被实例化为虚拟类型(X1,X2,...)
val revlists = fn : ?.X1 列表列表 -> ?.X1列表列表

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

- val revlists = (fn xs => 地图 rev xs);
val revlists = fn : '列表列表 -> '一个列表列表

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

- 有趣的 revlists xs = 地图 rev xs;
val revlists = fn : '列表列表 -> '一个列表列表

具有相同的结果。)在文献中,用 (fn x => ex) 替换函数值表达式 e 的技巧称为 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 ""

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

I didn't know the details of the value restriction, so I searched and found this article. Here is the relevant part:

Obviously, we aren't going to write the expression rev [] in a program, so it doesn't particularly matter that it isn't polymorphic. But what if we create a function using a function call? With curried functions, we do this all the time:

- val revlists = map rev;

Here revlists should be polymorphic, but the value restriction messes us up:

- 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

Fortunately, there is a simple trick that we can use to make revlists polymorphic. We can replace the definition of revlists with

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

and now everything works just fine, since (fn xs => map rev xs) is a syntactic value.
(Equivalently, we could have used the more common fun syntax:

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

with the same result.) In the literature, the trick of replacing a function-valued expression e with (fn x => e x) is known as eta expansion. It has been found empirically that eta expansion usually suffices for dealing with the value restriction.

To summarise, it doesn't look like higher-order programming is restricted so much as point-free programming. This might explain some of the trouble I have when translating Haskell code to F#.


Edit: Specifically, here's how to fix your first example:

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 ""

I haven't figured out the second one yet because the type constructor is getting in the way.

Saygoodbye 2024-09-05 04:16:37

这是这个问题的答案< /a> 在 F# 上下文中。
总而言之,在 F# 中,将类型参数传递给泛型(=多态)函数是一个运行时操作,因此它实际上是类型安全的泛化(例如,您不会在运行时崩溃)。然而,如此普遍化的价值行为可能会令人惊讶。

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

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

Here is the answer to this question in the context of F#.
To summarize, in F# passing a type argument to a generic (=polymorphic) function is a run-time operation, so it is actually type-safe to generalize (as in, you will not crash at runtime). The behaviour of thusly generalized value can be surprising though.

For this particular example in F#, one can recover generalization with a type annotation and an explicit type parameter:

type 'a SimpleType= F of (int ->'a-> 'a)
let get a = F(fun req -> id)  
let oops2<'T> : 'T SimpleType = get ""
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文