当参数为空列表时如何避免值限制错误?
How do I avoid the Value Restriction error when the argument is an empty list?
当参数为空列表时,List 模块中的某些函数会失败。 List.rev 就是一个例子。问题是可怕的价值限制。
我在尝试定义一个函数时遇到了同样的问题,该函数 returns 一个包含列表最后一个元素以外的所有元素的列表:
let takeAllButLast (xs: 'a list) =
xs |> List.take (xs.Length - 1)
该函数适用于非空列表,但处理空列表的版本失败:
let takeAllButLast (xs: 'a list) =
if List.isEmpty xs then []
else xs |> List.take (xs.Length - 1)
takeAllButLast []
error FS0030: Value restriction. The value 'it' has been inferred to have generic type
val it : '_a list, etc.
我尝试了几种方法:将其设为内联函数、不指定参数类型、指定返回值类型、使函数依赖于类型参数以及使用 Option 类型获取中间值结果稍后转换为列表<'a>。没有任何效果。
比如这个函数有同样的问题:
let takeAllButLast<'a> (xs: 'a list) =
let empty : 'a list = []
if List.isEmpty xs then empty
else xs |> List.take (xs.Length - 1)
之前在 SO 中提出了类似的问题:F# value restriction in empty list 但当参数为空列表时,唯一的答案也会失败。
有没有办法编写一个函数来处理空列表和非空列表?
注意:这个问题并不特定于 returns 除了列表的最后一个元素之外的所有函数。
函数本身完全没问题。函数没有"fail".
您不需要修改函数体。没错。
问题仅在于您尝试调用函数的方式:takeAllButLast []
。在这里,编译器不知道结果应该是什么类型。应该是string list
?还是应该int list
?也许 bool list
?编译器无法知道。所以它抱怨。
为了编译这样的调用,你需要帮助编译器:告诉它你希望得到什么类型。这可以从上下文中完成:
// The compiler gleans the result type from the type of receiving variable `l`
let l: int list = takeAllButLast []
// Here, the compiler gleans the type from what function `f` expects:
let f (l: int list) = printfn "The list: %A" l
f (takeAllButLast [])
或者直接声明调用表达式的类型:
(takeAllButLast [] : int list)
或者你可以声明函数的类型,然后调用它:
(takeAllButLast : int list -> int list) []
您也可以分两步完成此操作:
let takeAllButLast_Int : int list -> int list = takeAllButLast
takeAllButLast_Int []
在每种情况下,原则都是相同的:编译器需要从某个地方知道您在这里期望的类型。
或者,您可以给它起个名字,然后让那个名字变得通用:
let x<'a> = takeAllButLast [] : 'a list
这样的值可以像常规值一样访问,但在后台它被编译为无参数的通用函数,这意味着每次访问它都会导致其主体的执行。这就是 List.empty
和类似的 "generic values" 在标准库中的实现方式。
但是,当然,如果您尝试在 F# 交互式中评估这样的值,您将再次面临同样的陷阱 - 类型必须已知 - 无论如何您都必须解决它:
> x // value restriction
> (x : int list) // works
当参数为空列表时,List 模块中的某些函数会失败。 List.rev 就是一个例子。问题是可怕的价值限制。
我在尝试定义一个函数时遇到了同样的问题,该函数 returns 一个包含列表最后一个元素以外的所有元素的列表:
let takeAllButLast (xs: 'a list) =
xs |> List.take (xs.Length - 1)
该函数适用于非空列表,但处理空列表的版本失败:
let takeAllButLast (xs: 'a list) =
if List.isEmpty xs then []
else xs |> List.take (xs.Length - 1)
takeAllButLast []
error FS0030: Value restriction. The value 'it' has been inferred to have generic type
val it : '_a list, etc.
我尝试了几种方法:将其设为内联函数、不指定参数类型、指定返回值类型、使函数依赖于类型参数以及使用 Option 类型获取中间值结果稍后转换为列表<'a>。没有任何效果。
比如这个函数有同样的问题:
let takeAllButLast<'a> (xs: 'a list) =
let empty : 'a list = []
if List.isEmpty xs then empty
else xs |> List.take (xs.Length - 1)
之前在 SO 中提出了类似的问题:F# value restriction in empty list 但当参数为空列表时,唯一的答案也会失败。
有没有办法编写一个函数来处理空列表和非空列表?
注意:这个问题并不特定于 returns 除了列表的最后一个元素之外的所有函数。
函数本身完全没问题。函数没有"fail".
您不需要修改函数体。没错。
问题仅在于您尝试调用函数的方式:takeAllButLast []
。在这里,编译器不知道结果应该是什么类型。应该是string list
?还是应该int list
?也许 bool list
?编译器无法知道。所以它抱怨。
为了编译这样的调用,你需要帮助编译器:告诉它你希望得到什么类型。这可以从上下文中完成:
// The compiler gleans the result type from the type of receiving variable `l`
let l: int list = takeAllButLast []
// Here, the compiler gleans the type from what function `f` expects:
let f (l: int list) = printfn "The list: %A" l
f (takeAllButLast [])
或者直接声明调用表达式的类型:
(takeAllButLast [] : int list)
或者你可以声明函数的类型,然后调用它:
(takeAllButLast : int list -> int list) []
您也可以分两步完成此操作:
let takeAllButLast_Int : int list -> int list = takeAllButLast
takeAllButLast_Int []
在每种情况下,原则都是相同的:编译器需要从某个地方知道您在这里期望的类型。
或者,您可以给它起个名字,然后让那个名字变得通用:
let x<'a> = takeAllButLast [] : 'a list
这样的值可以像常规值一样访问,但在后台它被编译为无参数的通用函数,这意味着每次访问它都会导致其主体的执行。这就是 List.empty
和类似的 "generic values" 在标准库中的实现方式。
但是,当然,如果您尝试在 F# 交互式中评估这样的值,您将再次面临同样的陷阱 - 类型必须已知 - 无论如何您都必须解决它:
> x // value restriction
> (x : int list) // works