let inline (=~) a b = abs (single a - single b) <= 0.001f
type Vector =
{ x : single; y : single; z : single }
static member (=~) (v1, v2) = (v1.x =~ v2.x) && (v1.y =~ v2.y) && (v1.z =~ v2.z)
let v1, v2 =
{ x = 0.1f; y = single Math.PI; z = 0.f },
{ x = 0.1f; y = 3.14159f; z = 0.0001f }
v1 =~ v2
编译器抱怨:The type 'Vector' does not support a conversion to the type 'single'
我不明白。显然,特定类型的运算符并不优先于泛型运算符,从而扼杀了我的直觉。使这项工作的诀窍是什么?
答案 0 :(得分:4)
使用let
定义自定义运算符时,它始终优先于类型定义的运算符。处理这个问题的简单方法是避免本地和全局运算符名称中的名称冲突,或者将let
绑定运算符的范围保持在最小。例如,您可以将全局=~
运算符放在单独的模块中:
module VectorImplementation =
let inline (=~) a b = abs (single a - single b) <= 0.001f
module Vectors =
open VectorImplementation
type Vector =
{ x : single; y : single; z : single }
static member (=~) (v1, v2) =
(v1.x =~ v2.x) && (v1.y =~ v2.y) && (v1.z =~ v2.z)
open System
open Vectors
let v1, v2 =
{ x = 0.1f; y = single Math.PI; z = 0.f },
{ x = 0.1f; y = 3.14159f; z = 0.0001f }
v1 =~ v2
还可以使用somewhat strange hack 来定义重载的全局let
绑定运算符。关于这是否是一个好主意存在争议 - 我认为通常可以避免冲突而不诉诸这个魔术,但其他人可能不同意。
答案 1 :(得分:2)
F# 4.0 language specification的第97页说:
如果运算符未解析为用户定义或库定义的运算符,则名称解析规则(第14.1节)确保运算符解析为隐式使用涉及的静态成员调用表达式(第0节)的表达式操作数的类型。这意味着未在F#库中定义的运算符的有效行为是要求在运算符的一个操作数的类型上使用与运算符同名的静态成员。
正如Tomas Petricek刚才在答案中指出的那样,这意味着您全局定义的=~
运算符会“隐藏”=~
类型的Vector
运算符。他的回答表明了处理问题的好方法。另一种方法是简单地使两个运算符不同:
let inline (==~) a b = abs (single a - single b) <= 0.001f
type Vector =
{ x : single; y : single; z : single }
static member (=~) (v1, v2) =
(v1.x ==~ v2.x) && (v1.y ==~ v2.y) && (v1.z ==~ v2.z)
您可能会发现这种方法比Tomas的方法更简单,或者您可能会发现他的方法更简单。这是风格偏好的问题;任何一个都应该工作。