Generics 为什么自动泛化有时会推断出过于特定的类型?
当运算符的一侧具有已知类型而另一侧不具有已知类型时,某些函数用法不会编译。一个例子是测量单位:Generics 为什么自动泛化有时会推断出过于特定的类型?,generics,f#,inline,automatic-generalization,Generics,F#,Inline,Automatic Generalization,当运算符的一侧具有已知类型而另一侧不具有已知类型时,某些函数用法不会编译。一个例子是测量单位: let inline multiplyWithFive x = 5. * x type [<Measure>] myUnit let test = multiplyWithFive 3.<myUnit> // Compiler error 但这并不局限于度量单位。比如说,我创建了一个支持浮点非对称乘法的类型。它与multiplyWithFive函数不兼容,该函数任意推断其参
let inline multiplyWithFive x = 5. * x
type [<Measure>] myUnit
let test = multiplyWithFive 3.<myUnit> // Compiler error
但这并不局限于度量单位。比如说,我创建了一个支持浮点非对称乘法的类型。它与multiplyWithFive
函数不兼容,该函数任意推断其参数为float
:
type BoxFloat =
{ Value : float }
static member inline (*) (lhs : float, rhs : BoxFloat) =
{ Value = lhs * rhs.Value }
let boxThree = { Value = 3. }
let test2 = multiplyWithFive boxThree // Compiler error
同样,5.*boxThree
是一个有效的表达式。但自动概括似乎并不承认这一点
我可以用度量单位感知的类型注释“修复”第一种情况,但这没有明显的原因限制了底层类型。如果我真的需要一个更通用的函数,我不知道如何阻止编译器设置限制。如果我显式命名泛型参数,它只会拒绝将其保持为泛型:
// Warning: This construct causes code to be less generic than indicated...
let inline multiplyWithFive (x : 'T) = 5. * x
我能做些什么?有什么方法可以说我想要更通用的版本吗?为了回答你的第一个问题,我认为F#编译器不会自动泛化泛型类型以包含可能的单元,因此你的第一个示例最终采用了
float
。如果在类型批注中指定一个浮点值,则它将在单位上通用化:
let inline multiplyWithFive (x:float<_>) = 5. * x
type [<Measure>] myUnit
multiplyWithFive 3. // Works fine without units
multiplyWithFive 3.<myUnit> // Works fine with units
您可能可以用一种更为奇特的方式使用静态成员约束来解决这个问题——使用静态memebrs有一种方法。然而,我认为这会稍微扩展机制,可能会对代码的其他部分产生可用性影响。ooh!这是某些基本类型和运算符的特例!如果使用自定义类型而不是float,则行为会有所不同!我认为这个答案的第一部分是第二部分的一个特例。如
中所示,让内联乘法a b=a*b
,编译器不一定介意对度量单位进行泛化。并给出了一个类型M
let inline multiplyWithFive (x:float<_>) = 5. * x
type [<Measure>] myUnit
multiplyWithFive 3. // Works fine without units
multiplyWithFive 3.<myUnit> // Works fine with units
// warning FS0077: Member constraints with the name 'op_Multiply'
// are given special status by the F# compiler
let inline amultiplyWithFive (x:^T) : ^R =
(^T : (static member (*) : float * ^T -> ^R) (5.0, x))
// no warning since we're requiring operator **
let inline multiplyWithFive (x:^T) : ^R =
(^T : (static member ( ** ) : float * ^T -> ^R) (5.0, x))