types - 令人惊讶的(对我来说)简单 F# 函数的类型
问题描述
我正在编写如下 F# 函数来计算数字列表的乘积,其中包含一个累加器“r”。
> let rec proda xs r =
- match xs with
- | [] -> r
- | x::xr -> proda xr (x*r)
- ;;
令我惊讶的是proda
,根据 F# 控制台,它的类型是:
val proda : xs:int list -> r:int -> int
为什么不是类型proda
val proda : xs:a' list -> r:a' -> a'
解决方案
参数的类型被推断为int
,因为您正在使用*
运算符来聚合 中列表的元素x * r
。这决定了x
andr
的类型int
,因此,xs
将变为int list
。
如果你想避免这种情况,你可以用*
作为额外参数给出的函数替换:
let rec proda f xs r =
match xs with
| [] -> r
| x::xr -> proda f xr (f x r)
这具有以下类型:
val proda : f:('a -> 'b -> 'b) -> xs:'a list -> r:'b -> 'b
这是一个非常有用的函数,它恰好包含在核心 F# 库中List.fold
(最后两个参数已切换)!它更通用一些,因为结果可以是与列表元素不同的类型(a
vs. b
),但它是对您所拥有的内容的简单概括。
编辑你是对的,*
也可以与float
. 为了支持这一点,您必须制作函数inline
. 这有点棘手,因为你不能用递归函数来做到这一点,所以你需要添加一个嵌套的递归函数:
let inline proda xs r =
let rec loop xs r =
match xs with
| [] -> r
| x::xr -> loop xr (x*r)
loop xs r
使用内联函数,编译器可以推断“静态成员约束”,这本质上是一种表示“任何支持的类型*
”的方式。语法有点冗长:
val inline proda :
xs: ^a list -> r: ^b -> ^b
when ( ^a or ^b) : (static member ( * ) : ^a * ^b -> ^b)