首页 > 解决方案 > 令人惊讶的(对我来说)简单 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'

标签: typesf#

解决方案


参数的类型被推断为int,因为您正在使用*运算符来聚合 中列表的元素x * r。这决定了xandr的类型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(最后两个参数已切换)!它更通用一些,因为结果可以是与列表元素不同的类型(avs. 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)

推荐阅读