2010-05-11 7 views
10

문자열에 대해 F #에서 (/) 연산자를 오버로드하고 숫자의 의미를 유지하려고합니다. 내가 얻을 다음을 시도 할 경우F #의 오버로드 연산자 : (/)

/// Combines to path strings 
let (/) path1 path2 = Path.Combine(path1,path2) 

let x = 3/4 // doesn't compile 

"연산자 오버로드를 제공 할 수 없습니다 (29 개) 확장 회원 경고. 대신 유형 정의의 일부로 연산자를 정의하는 것이 좋습니다."

/// Combines to path strings 
type System.String with 
    static member (/) (path1,path2) = Path.Combine(path1,path2) 

아이디어가 있으십니까?

감사합니다, forki

+0

당신이 op_Division로 정의하는 시도? 업데이트 : 신경 쓰지 마세요; 작동하지 않을 것입니다. – pblasucci

+0

기존 클래스에 멤버 (또는 연산자)를 오버로드 할 수 있다고 생각하지 않습니다. – Gabe

답변

18

당신은 기존의 유형에 대한 오버로드 연산자를 제공 할 수 없습니다. 하나의 옵션은 다른 연산자 이름을 사용하는 것입니다 (Natahan이 제안한대로). 종류가 더 명시함으로써, 당신은 유형 검사를 제공 -

open System  

// Simple type for representing paths 
type Path(p) = 
    // Returns the path as a string 
    member x.Path = p 
    // Combines two paths 
    static member (/)(p1:Path, p2:Path) = 
    Path(IO.Path.Combine(p1.Path, p2.Path)) 

let n = 4/2 
let p = Path("C:\\")/Path("Temp") 

이 하나의 중요한 이점이있다 : 그러나, 당신은 또한 당신의 F # 코드의 경로를 대표하고이 유형의 / 연산자를 제공 할 수있는 새로운 유형을 정의 할 수 있습니다 코드를 확인하는 데 사용할 수있는 자세한 정보. 문자열을 사용하여 경로를 표현하면 경로를 다른 문자열 (예 : 이름)과 쉽게 혼동 할 수 있습니다. Path 유형을 정의하면 유형 검사기가이 실수를하지 않게됩니다.

또한, 컴파일러는 p + p가 정의되어 있지 않기 때문에 (제대로 Path.Combine를 사용하는 경우에만 /를 사용할 수 있습니다), (단순히) (당신이 문자열로 경로를 나타낼 경우 쉽게 일어날 수있는) 잘못 경로를 결합 당신을 허용하지 않습니다 .

+2

주제에 관한 책을 저술 한 사람이 질문에 대답하는 것이 좋습니다! –

+1

Hrm, p1 : Path, P2 : string overload를 제공하여이 라인의 구문은 어떻게됩니까? (p = Path "C : \\"/ "Temp" –

5

나는이 the overloading documentation의 독서를 기반으로, F #으로 가능하다고 생각하지 않습니다.

대신 /과 같은 고유 한 함수를 만드는 것이 좋습니다. 뭔가 같은 :

let (</>) path1 path2 = Path.Combine (path1,path2) 

이 그것이 인간의 독자가 /이 결과는 부동 소수점 것을 의미 running-- 것을 암시 타입 추론과 혼란을하지 않기 때문에 장기적으로 덜 성가신 될 가능성이 높습니다, 때로는 문자열 인 것을 기억하는 것은 부담입니다 *. 그러나 독자가 처음으로 </>을 본 후에는 중간에 삽입 된 기호와 관련이 있다는 것을 쉽게 기억할 수 있습니다.

* 문자열에 대한 유일한 이유는 +이 과다 노출이라고 생각합니다. 오랫동안 Haskell이나 Caml을 사용한 후에 다른 언어로 전환 한 후 처음 몇 분이 지나면 "foo" + "bar"이보기 흉하게 보입니다.

+0

현재 (@@)를 사용하고 있습니다 만, (/)가 더 좋을 것이라고 생각합니다. – forki23

8

나는 그것을하는 간단한 방법이 없다고 생각합니다. 확장 멤버는 F #의 연산자 오버로딩에 고려되지 않으며 멤버 제약 조건을 사용하는 준 일반 방식으로 작업을 재정의하는 좋은 방법이 아닙니다.

이 작동 할 함께 뭔가를 해킹 할 수 있지만 매우 추한 :

type DivisionOperations = 
    static member Divide(x:int, y:int) = x/y 
    static member Divide(path1, path2) = Path.Combine(path1, path2) 

let inline div< ^t, ^a, ^b, ^c when (^t or ^a) : (static member Divide : ^a * ^b -> ^c)> a b = ((^t or ^a) : (static member Divide : ^a * ^b -> ^c) (a, b)) 

let inline (/) x y = div<DivisionOperations, _, _, _> x y 
8

실제로 할 수 있습니다.

이 시도 :

open System.IO 

type DivExtension = DivExtension with 
    static member inline (=>) (x    , DivExtension) = fun y -> x/y 
    static member  (=>) (x    , DivExtension) = fun y -> Path.Combine(x, y) 
    static member  (=>) (x:DivExtension, DivExtension) = fun DivExtension -> x 

let inline (/) x y = (x => DivExtension) y 
+2

사실 이것은 컴파일러가'(/)를 기본값으로 설정하지 못하도록 방지하면서 더미'DivExtension * DivExtesion -> DivExtension -> DivExtension' 오버로드를 추가하여 첫 번째 오버로드를 일반화 할 수 있었지만 실제로는 내 대답과 비슷합니다. '연산자를 두 번째 과부하로 설정하는 것은 매우 영리합니다. 나는 당신이'(=>)'기호 연산자를 사용하여 명시 적 정적 멤버 제약을 생략하더라도'(/)'의 본문을 이해하기 어렵게 만든다고 생각합니다. – kvb

+0

예, 당신 말이 맞아요. 정적 제약을 쓰는 것을 피하기 위해 중간 연산자를 사용하는 것을 선호합니다. 귀하의 대답에 더미 오버로드를 추가 할 수 있으며 일반화됩니다. 내가 연산자없이 할 수없는 한 가지는 '또는'를 두 번 쓰는 것입니다. (^ t 또는^a 또는^b)와 같은 것을 의미합니다. 삼항 연산자를 사용하는 경우에는 이것이 유일한 방법이라고 생각합니다. – Gustavo

+0

안녕하세요, 테스트 및 작동하지만 정말 이해가 안돼요 (F #에 익숙하지 않습니다). – Liviu