gpt4 book ai didi

f# - 为什么这会混淆 F# 编译器的类型推断?

转载 作者:行者123 更新时间:2023-12-04 23:14:55 26 4
gpt4 key购买 nike

这里没有问题:

module Seq =
let private rnd = Random Environment.TickCount

let random =
fun (items : 'T seq) ->
let count = Seq.length items
items |> Seq.nth (rnd.Next count)

签名 Seq.randomitems:seq<'T> -> 'T .都好。

是的,我知道我可以 let random items = [...] ,这不是重点。

重点是 items突然被限制为类型 seq<obj>当我这样做时:
module Seq =
let random =
let rnd = Random Environment.TickCount
fun (items : 'T seq) ->
let count = Seq.length items
items |> Seq.nth (rnd.Next count)

... 即我添加 Random对象作为闭包。如果我将鼠标悬停在 random ,Intellisense 显示签名变成了 items:seq<obj> -> obj .

有趣的是,如果我选择代码并点击 [Alt]+[Enter]要在 F# Interactive 中执行它,签名显示为 seq<'a> -> 'a .什么??

那么,这里发生了什么?为什么类型推断会出现困惑和不一致?

最佳答案

这是由于所谓的Value Restriction .长话短说,句法值不能是通用的,因为它可能会在发生突变时破坏事物,并且编译器不能总是可靠地证明不变性。 (请注意,尽管 random 在语义上是一个函数,但在语法上它仍然是一个值,这才是最重要的)

但有时编译器可以证明不变性。这就是您的第一个示例有效的原因:当 let 的右侧时是一个直接的 lambda 表达式,编译器可以肯定地告诉它它是不可变的,所以它让这个通过。

另一个例子是 let x = [] - 这里编译器可以看到 nil 列表 []是不可变的。另一方面,let x = List.append [] []不会工作,因为在这种情况下编译器无法证明不变性。

这种对值限制的“放宽”是在 F# 中逐案完成的。 F# 编译器只能处理一些特殊情况:文字、lambda 表达式等,但它没有一个成熟的机制来证明一般的不变性。这就是为什么一旦你走出这些特殊情况,你就不能拥有通用值。

您可以通过添加显式类型参数在技术上克服这一点。从逻辑上讲,这告诉编译器“是的,我知道这是一个通用值,这就是我的意思”。

let random<'t> : seq<'t> -> 't =
let rnd = Random Environment.TickCount
fun items ->
let count = Seq.length items
items |> Seq.nth (rnd.Next count)

let x = random [1;2;3]

但这仍然不会做你想要的,因为在幕后,这样的定义将被编译为一个无参数的泛型方法,每次你引用这样的“值”时,该方法都会被调用并返回一个新函数 - 带有全新 rnd为每次通话而烘烤。换句话说,上面的代码将等价于:
let random() =
let rnd = Random Environment.TickCount
fun items ->
let count = Seq.length items
items |> Seq.nth (rnd.Next count)

let x = random() [1;2;3]

关于f# - 为什么这会混淆 F# 编译器的类型推断?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44992639/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com