当运算符的一侧具有已知类型而另一侧不具有已知类型时,某些函数用法不会编译。一个例子是计量单位:
let inline multiplyWithFive x = 5. * x
type [<Measure>] myUnit
let test = multiplyWithFive 3.<myUnit> // Compiler error
5. * 3.<myUnit>
显然是一个有效的表达式,所以这是令人惊讶的,特别是考虑到inline
函数在其他情况下最大化概括:
let inline multiply a b = a * b
let test = multiply 5. 3.<myUnit> // Valid
但这并不仅限于计量单位。比方说,我创建了一个支持浮点数不对称乘法的类型。它与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
是一个有效的表达式。但是自动概括似乎没有承认这一点。
我可以使用度量单位感知类型注释来“修复”第一个案例,但这无条件地限制了基础类型。如果我真的需要一个更通用的功能,我不知道如何阻止编译器制定限制。如果我明确地命名一个泛型参数,它只是拒绝保持通用:
String.split "from" "target string: blah... from ..."
|> String.join "to"
我该怎么办?有没有办法说我想要更通用的版本?
答案 0 :(得分:5)
要回答您的第一个问题,我认为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
至于使用支持乘法运算符的任何类型使这个工作 - 我认为F#编译器是乘法运算符的特殊外壳,因此它在典型的情况下提供合理的默认行为,你真的只想使用{{1 }或float
值。如果使用显式成员约束来定义它,则警告非常清楚:
float<_>
你可以通过更加花哨的方式使用静态成员约束来解决这个问题 - 有一个trick that lets you define overloaded operators使用静态memebrs。但是,我认为这会稍微扩展机制,并且可能会对代码的其他部分产生可用性影响。