“价值限制”是否有效?实际上意味着没有高阶函数式编程?
“值限制”实际上意味着不存在高阶函数式编程吗?
我有一个问题,每次我尝试做一点 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
绝对不是!值限制几乎不会干扰高阶函数式编程。它的作用是在顶层限制多态函数的某些应用(而不是高阶函数)。
让我们看看你的例子。
您的问题是
oops
和oops2
都是恒等函数并且具有类型forall 'a 。 '一-> 'a
.换句话说,每个都是一个多态值。但右边并不是所谓的“句法值”;它是一个函数应用程序。 (函数应用程序不允许返回多态值,因为如果是这样,您可以使用可变引用和列表构造一个黑客函数,这会颠覆类型系统;也就是说,您可以编写一个终止函数类型 typeforall 'a 'b 'a -> 'b
幸运的是,在几乎所有实际情况中,所讨论的多态值都是一个函数,您可以通过 eta 扩展来定义它:
这个习惯用法看起来有一些运行时成本,但取决于内联器和优化器,编译器可以消除它 - 只是糟糕的类型检查器遇到了麻烦,
oops2
示例更麻烦,因为您必须这样做 。打包和解包值构造函数:这相当繁琐,但匿名函数
fun x -> ...
是一个语法值,而F
是一个数据类型构造函数,应用于语法值的构造函数也是语法值,而Bob是你的叔叔。F
的打包和解包都会被编译到恒等函数中,所以。 oops2
将编译成与oops
完全相同的机器代码。当您希望运行时计算返回像
None
或[]
这样的多态值时,事情会更糟糕。正如 Nathan Sanders 所暗示的,您可以使用像rev []
这样简单的表达式来违反值限制:没有更高阶的东西!但价值限制仍然适用。
在实践中值的限制不会对高阶函数的定义和使用造成障碍;你只需进行 eta 扩展即可。
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
andoops2
are both the identity function and have typeforall '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 typeforall '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:
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:This is quite a but more tedious, but the anonymous function
fun x -> ...
is a syntactic value, andF
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 ofF
is all going to be compiled into the identity function, sooops2
is going to compile into exactly the same machine code asoops
.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 asrev []
: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.
我不知道值限制的详细信息,所以我搜索并找到 这篇文章。这是相关部分:
总而言之,高阶编程似乎不像无点编程那样受到限制。这也许可以解释我在将 Haskell 代码转换为 F# 时遇到的一些问题。
编辑:具体来说,这里是如何修复第一个示例:
我还没有弄清楚第二个示例,因为类型构造函数妨碍了。
I didn't know the details of the value restriction, so I searched and found this article. Here is the relevant part:
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:
I haven't figured out the second one yet because the type constructor is getting in the way.
这是这个问题的答案< /a> 在 F# 上下文中。
总而言之,在 F# 中,将类型参数传递给泛型(=多态)函数是一个运行时操作,因此它实际上是类型安全的泛化(例如,您不会在运行时崩溃)。然而,如此普遍化的价值行为可能会令人惊讶。
对于 F# 中的这个特定示例,可以使用类型注释和显式类型参数来恢复泛化:
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: