2017-08-07 138 views
4
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 

编译F#运营商抱怨:The type 'Vector' does not support a conversion to the type 'single'不能正确解析

我不明白这一点。显然,特定于类型的运算符不会优先于泛型运算符,这会挫败我的直觉。做这项工作有什么窍门?

回答

4

当您使用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结合的运营商。对于这是不是一个好主意,我们有一些争议 - 我认为通常可以避免冲突而不诉诸这个魔术,但其他人可能会不同意。

2

页97 F# 4.0 language specification的说:

如果操作员不能解决用户定义的或者库定义的运营商,名称解析规则(§14.1)保证操作解析为表达式隐式使用涉及操作数类型的静态成员调用表达式(第0节)。这意味着未在F#库中定义的运算符的有效行为是要求运算符的一个操作数的类型与运算符具有相同名称的静态成员。

正如托马斯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) 

你会发现这个方法比托马斯的做法比较简单,或者你会发现他的方法更简单。这是一个风格偏好的问题;任何人都应该工作。