f# - 了解 F# 值限制错误

标签 f# type-inference value-restriction

我不明白 F# 中的值限制是如何工作的。我已经阅读了 wiki 中的解释以及 MSDN documentation .我不明白的是:

  1. 为什么,例如,这会给我一个值限制错误(摘自 this 问题):

    let toleq (e:float<_>) a b = (abs ( a - b ) ) < e
    

    但事实并非如此:

    let toleq e (a:float<_>) b = (abs ( a - b ) ) < e
    
  2. 这是普遍化的好吧...

    let is_bigger a b = a < b
    

    但这不是(它被指定为 int):

    let add a b = a + b
    
  3. 为什么带有隐式参数的函数会产生值限制:

    这个:

    let item_count = List.fold (fun acc _ -> 1 + acc) 0
    

    对比这个:

    let item_count l = List.fold (fun acc _ -> 1 + acc) 0 l
    

    (请注意,如果我确实在代码片段中使用了这个函数,VR 错误就会消失,但是函数将被指定为我使用它的类型,我希望它被泛化)

它是如何工作的?

(我使用的是最新的 F#,v1.9.6.16)

最佳答案

编辑

更好/最近的信息在这里:Keeping partially applied function generic

(以下为原文)

我认为这里务实的做法是不要试图深入理解这一点,而是要了解一些通用策略以通过 VR 并继续您的工作。这是一个有点“逃避”的答案,但我不确定在这里花时间了解 F# 类型系统的内部结构(从一个版本到另一个版本,它会继续以微小的方式发生变化)是否有意义。

我提倡的两个主要策略是这些。首先,如果您使用函数类型定义一个值(类型带有箭头“->”),则通过执行 eta-conversion 确保它是一个句法函数。 :

// function that looks like a value, problem
let tupleList = List.map (fun x -> x,x)
// make it a syntactic function by adding argument to both sides
let tupleList l = List.map (fun x -> x,x) l

其次,如果您仍然遇到 VR/泛化问题,请指定整个类型签名来说明您想要的内容(然后在 F# 允许的情况下“退出”):

// below has a problem...
let toleq (e:float<_>) a b = (abs ( a - b ) ) < e
// so be fully explicit, get it working...
let toleq<[<Measure>]'u> (e:float<'u>) (a:float<'u>) (b:float<'u>) : bool = 
    (abs ( a - b ) ) < e
// then can experiment with removing annotations one-by-one...
let toleq<[<Measure>]'u> e (a:float<'u>) b = (abs ( a - b ) ) < e

我认为这两种策略是最实用的建议。也就是说,这是我尝试回答您的具体问题。

  1. 我不知道。

  2. '>' 是一个完全通用的函数 ('a -> 'a -> bool),适用于所有类型,因此 is_bigger 具有泛化性。另一方面,“+”是一个“内联”函数,适用于少数基本类型和其他类型的某个类;它只能在其他“内联”函数中泛化,否则必须固定为特定类型(或默认为“int”)。 (即席多态性的“内联”方法是 F# 中的数学运算符克服“类型类”不足的方式。)

  3. 这就是我上面讨论的“语法功能”问题; '让我们编译成字段/属性,与函数不同,它不能是通用的。因此,如果您希望它是通用的,请将其设为函数。 (另请参阅 this question 了解此规则的另一个异常(exception)情况。)

关于f# - 了解 F# 值限制错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23201186/

相关文章:

f# - 我们可以使用更通用的类型来缩短 F# 中的这段代码吗?

list - 使用foldBack 将字符列表转换为字符串

polymorphism - 如何柯里化(Currying)带有可选参数的函数,以在 ReasionML/BuckleScript 中生成 Js.t 对象?

scala - Dotty 如何决定如何推断/何时扩大联合类型?

java - MultiSet.Entry 和泛型的比较器

ocaml - 无法输入多态 [%bs.raw 函数

lambda - 为什么`id id`在OCaml中不是值?

.net - 您可以将 Thread.CurrentPrincipal 与 F# 异步工作流程一起使用吗?

c# - F# 命名空间/模块和与 C# 的互操作

typescript - 键在应该用于索引类型时却不能使用