F#类型推断和某种通用的函数参数

F#类型推断和某种通用的函数参数,f#,F#,我的函数如下所示: let inline fmingd f' x0 alpha = let rec step x i = if i >= 100 then x else step (x - alpha*(f' x)) (i+1) step x0 0 let inline fmingd (f':^a -> ^a) (x0:^a) (alpha:float) : ^a when ^a : (static member ( * ) : float * ^a

我的函数如下所示:

let inline fmingd f' x0 alpha =
  let rec step x i =
    if i >= 100 then x
    else step (x - alpha*(f' x)) (i+1)
  step x0 0
let inline fmingd (f':^a -> ^a) (x0:^a) (alpha:float) : ^a
    when ^a : (static member ( * ) : float * ^a -> ^a) and
         ^a : (static member ( - ) : ^a * ^a -> ^a) =
  let rec step x i =
    if i >= 100 then x
    else step (x - alpha*(f' x)) (i+1)
  step x0 0
它足够通用,可以使用浮动:

let f' x = 4.*x + 3.
let fminx = fmingd f' 0. 0.1
。。。和(MathNet.Numeric)向量:

这应该是可行的,因为向量有带浮点->向量->向量的操作符(*),和带向量->向量->向量的操作符(-)

但一旦我这样做了,函数就变成非泛型的。。它只接受并返回浮动。其类型签名变为:

val inline fmingd : f':(float -> float) -> x0:float -> alpha:float -> float
在将alpha限制为浮动的同时,如何保持f',x0和fmingds返回值的通用性

为了给F#编译器留下深刻印象,我提出了以下建议:

let inline fmingd f' x0 alpha =
  let rec step x i =
    if i >= 100 then x
    else step (x - alpha*(f' x)) (i+1)
  step x0 0
let inline fmingd (f':^a -> ^a) (x0:^a) (alpha:float) : ^a
    when ^a : (static member ( * ) : float * ^a -> ^a) and
         ^a : (static member ( - ) : ^a * ^a -> ^a) =
  let rec step x i =
    if i >= 100 then x
    else step (x - alpha*(f' x)) (i+1)
  step x0 0
。。。但在alpha*(f'x)它仍然告诉我:

“此构造导致代码不像 类型批注。类型变量“a”已被约束为类型 “浮动”

。。这将产生与上述相同的全浮点类型签名。

看起来(*)是经过特殊处理的。将代码更改为以下格式:

let inline fmingd (f':^a -> ^a) (x0:^a) (alpha:float) : ^a
    when ^a : (static member times : float * ^a -> ^a) and
         ^a : (static member ( - ) : ^a * ^a -> ^a) =
  let rec step x i =
    if i >= 100 then x
    else step (x - (^a : (static member times : float * ^a -> ^a) (alpha, f' x))) (i+1)
  step x0 0
然后当我试着这样做的时候:

let inline fmingd (f':^a -> ^a) (x0:^a) (alpha:float) : ^a
    when ^a : (static member ( * ) : float * ^a -> ^a) and
         ^a : (static member ( - ) : ^a * ^a -> ^a) =
  let rec step x i =
    if i >= 100 then x
    else step (x - (^a : (static member ( * ): float * ^a -> ^a) (alpha, f' x))) (i+1)
  step x0 0
我回到了你最初的错误,但也得到了这个错误:

名为“op#u Multiply”的成员约束由F#编译器赋予特殊状态,因为某些.NET类型隐式地增加了该成员。如果试图从自己的代码中调用成员约束,则可能会导致运行时失败

不幸的是,我不确定如果您想使用其他人的类型,那么调用所需的成员
times
是否会对您太有效

编辑:

我想这可能是因为

14.5.4.1构件约束解决方案的模拟

假定某些类型隐式定义静态成员,即使类型的实际CLI元数据没有定义这些运算符。该机制用于实现F#库的可扩展转换和数学函数,包括sin、cos、int、float、(+)和(-)。下表显示了为各种类型隐式定义的静态成员

它接着提到了
float
,它是有
op\u Multiply
(即
(*)
)专门定义的事物之一。

看起来(*)是经过特殊处理的。将代码更改为以下格式:

let inline fmingd (f':^a -> ^a) (x0:^a) (alpha:float) : ^a
    when ^a : (static member times : float * ^a -> ^a) and
         ^a : (static member ( - ) : ^a * ^a -> ^a) =
  let rec step x i =
    if i >= 100 then x
    else step (x - (^a : (static member times : float * ^a -> ^a) (alpha, f' x))) (i+1)
  step x0 0
然后当我试着这样做的时候:

let inline fmingd (f':^a -> ^a) (x0:^a) (alpha:float) : ^a
    when ^a : (static member ( * ) : float * ^a -> ^a) and
         ^a : (static member ( - ) : ^a * ^a -> ^a) =
  let rec step x i =
    if i >= 100 then x
    else step (x - (^a : (static member ( * ): float * ^a -> ^a) (alpha, f' x))) (i+1)
  step x0 0
我回到了你最初的错误,但也得到了这个错误:

名为“op#u Multiply”的成员约束由F#编译器赋予特殊状态,因为某些.NET类型隐式地增加了该成员。如果试图从自己的代码中调用成员约束,则可能会导致运行时失败

不幸的是,我不确定如果您想使用其他人的类型,那么调用所需的成员
times
是否会对您太有效

编辑:

我想这可能是因为

14.5.4.1构件约束解决方案的模拟

假定某些类型隐式定义静态成员,即使类型的实际CLI元数据没有定义这些运算符。该机制用于实现F#库的可扩展转换和数学函数,包括sin、cos、int、float、(+)和(-)。下表显示了为各种类型隐式定义的静态成员


它接着提到了
float
,它是专门定义的
op\u Multiply
(即
(*)
)的东西之一。

正如编译器指出的,问题在于这个片段:
alpha*(f'x)

这是因为alpha是一个
浮点
,在f#中,乘法具有一个
浮点
,因为第一个操作数期望另一个
浮点
,所以它推断第二个操作数为
浮点

如果第二个操作数不是泛型的,即:int,您可以使用如下显式转换:
int alpha*(f'x)
,但无法表示对f#中泛型类型的泛型
op_explicit
的调用,您可以尝试使用成员约束,但Ganesh在F#spec中指出的相同问题也适用于
op#u Explicit

我在过去尝试模拟Haskell的通用数学时遇到了同样的问题,特别是通用函数
fromInteger
,但是,您可以使用相同的技术创建一个通用
fromFloat
函数,或者使用一个通用
convert
函数,就像我刚才添加到的函数一样

示例代码:

#r @"FsControl.Core.dll"
#r @"FSharpPlus.dll"

open FSharpPlus

let inline fmingd (f':^a -> ^a) (x0:^a) (alpha:float) : ^a =
  let rec step x i =
    if i >= 100 then x
    else step (x - convert alpha * (f' x)) (i+1)
  step x0 0

正如编译器所指出的,问题在于这个片段:
alpha*(f'x)

这是因为alpha是一个
浮点
,在f#中,乘法具有一个
浮点
,因为第一个操作数期望另一个
浮点
,所以它推断第二个操作数为
浮点

如果第二个操作数不是泛型的,即:int,您可以使用如下显式转换:
int alpha*(f'x)
,但无法表示对f#中泛型类型的泛型
op_explicit
的调用,您可以尝试使用成员约束,但Ganesh在F#spec中指出的相同问题也适用于
op#u Explicit

我在过去尝试模拟Haskell的通用数学时遇到了同样的问题,特别是通用函数
fromInteger
,但是,您可以使用相同的技术创建一个通用
fromFloat
函数,或者使用一个通用
convert
函数,就像我刚才添加到的函数一样

示例代码:

#r @"FsControl.Core.dll"
#r @"FSharpPlus.dll"

open FSharpPlus

let inline fmingd (f':^a -> ^a) (x0:^a) (alpha:float) : ^a =
  let rec step x i =
    if i >= 100 then x
    else step (x - convert alpha * (f' x)) (i+1)
  step x0 0