2013-02-23 2 views
3

다음 코드를 고려해이 같은 오류를 제공하지만, -(aInherited :> Base)cos (aInherited :> Base) 작업을 수행 cos aInherited상속 된 형식에서 단항 부정 및 수학 함수가 작동하도록하려면 어떻게해야합니까?

error FS0001: This expression was expected to have type 
    Inherited  
but here has type 
    Base  

같은 :

type Base(x : float) = 
    member this.x = x 
    static member (~-) (a : #Base) = Base(-a.x) 
    static member Cos (a : #Base) = Base(cos a.x) 

type Inherited(x : float) = 
    inherit Base(x) 

let aBase = Base(5.0) 
let aInherited = Inherited(5.0) 

-aBase    // OK, returns Base(-5.0) 
-(aInherited :> Base) // OK, returns Base(-5.0) 
-aInherited   // not OK 

마지막 줄에서 오류가 발생합니다.

오류 메시지는 이러한 함수가 - 또는 cos의 반환 형식이 인수 형식과 같을 것을 제안합니다. 이것은 지나치게 까다로운 요구 사항처럼 보입니다.

  • 연산자를 정의하는 기본 형식을 상속하는 클래스의 경우 모든 연산자를 재정의하지 않으면 불가능합니다.
  • 이러한 클래스는 당신이 제어하지 않는 이상 외부 라이브러리에있는 경우
  • , 당신의 선택은 더욱 제한되어 있습니다.

이 주위에 방법이 있나요? F # 소스 코드에서 cos 함수는 prim-types.fs에 정의되어 있습니다.

답변

4

나는 그렇게 할 방법이 없다고 생각합니다.

이 연산자의 원본 전역 정의의 서명은 입력과 동일한 유형을 반환하므로 전역 정의를 재정의하지 않으면이 서명을 따르지 않는 정적 멤버를 추가 할 수 없습니다.

제한된 서명으로 새로운 전역 정의를 작성하면 모든 경우를 처리해야합니다. 그렇지 않으면 전체 정의를 재사용 할 수있는 유일한 방법은 중간 유형을 통과하고 유형 유추를 시도하는 것입니다.

type Base(x : float) = 
    member this.x = x 

type Inherited(x : float) = 
    inherit Base(x) 

type UnaryNeg = UnaryNeg with 
    static member inline ($) (UnaryNeg, a  ) = fun (_  ) -> -a 
    static member  ($) (UnaryNeg, a: #Base) = fun (_:UnaryNeg) -> Base(-a.x) 
let inline (~-) a = (UnaryNeg $ a) UnaryNeg 

type Cos = Cos with 
    static member inline ($) (Cos, a  ) = fun (_ ) -> cos a 
    static member  ($) (Cos, a: #Base) = fun (_:Cos) -> Base(cos a.x)  
let inline cos a = (Cos $ a) Cos 

이 모든 경우 및 자료의 파생 유형에 대해 작동합니다

> cos 0.5 ;; 
val it : float = 0.8775825619 
> cos (Base 0.5) ;; 
val it : Base = FSI_0002+Base {x = 0.8775825619;} 
> cos (Inherited 0.5) ;; 
val it : Base = FSI_0002+Base {x = 0.8775825619;} 
> type Inherited2(x : float) =  inherit Base(x) ;; 
> cos (Inherited2 0.5) ;; 
val it : Base = FSI_0002+Base {x = 0.8775825619;} 
2

그것은 훨씬 더 흥미로운 가져옵니다. 상속 된 형식의 연산자를 재정의하고 기본 클래스 연산자를 호출하는 데 사용하는 빠르고 해킹 된 솔루션을 사용할 수 있다고 생각했지만 상속 된 형식의 연산자를 정의한 후에도 여전히 오류 메시지가 표시됩니다. 마지막 케이스 (정말로 이상하다).

type Inherited(x : float) = 
    inherit Base(x) 

    static member (~-) (a : Inherited) = 
     -(a :> Base) 

    static member Cos (a : Inherited) = 
     cos (a :> Base) 

대신 원래 하나의 정의를 사용하는 경우가 적어도 당신이 연산자를 사용할 수 있도록해야 - 대신은 ('자료'의 인스턴스를 기대 대해 동일한 오류 메시지를 제공하는 매우 이상하다).

내 생각 엔 당신이 컴파일러의 버그를 발견하거나 언어 사양 적어도 가장자리 경우 한 것입니다. 이것을 fsbugsmicrosoft.com으로 이메일을 보내야 다음 버전에서 문제를 해결할 수 있습니다.

+0

내가 그것을 버그 생각하지 않습니다,이 방법 사업자와 F 번호 거래, 글로벌 범위에서 첫 모습입니다, 두 가지 유형을 모두 강제하는 특정 서명이있는 전역 정의를 찾습니다. 똑같다. 따라서 오류 메시지. – Gustavo

+0

@Gustavo 그런 경우라면,이 동작이 예상됩니다. 더 나은 오류 메시지가있을 것입니다. 상속 된 연산자 또는 무엇이든에 관한 것입니다. 표준 입력 관련 오류 메시지를 재사용하기 때문에 간과 된 연산자 오버로딩의 뚜렷한 문제인 것처럼 보입니다. –

2

당신은 모듈에 연산자를 넣을 수 있습니다 :

module BaseOps = 
    let (~-) (a: #Base) = Base(-a.x) 
    let cos (a: #Base) = Base(cos a.x) 

open 모듈이 내장 된 사업자 그림자 할 수 있습니다. 그런 다음 더 이상 예상되는 서명에 제약을받지 않습니다 (잠재적 인 버그를 회피합니다). 이것은 checked operators에 대해 Core lib에서 사용 된 것과 동일한 기술입니다.

open BaseOps 

let aInherited = Inherited(5.0) 
cos aInherited // OK 
-aInherited // OK 
+0

해결 방법에 대해서는 좋은 생각이지만, 'cos 5.0'과 같은 것은 실패합니다. 'float'유형이 'Base'유형과 호환되지 않습니다. 정확히 원하는 것이 아닙니다. –

+0

@JeffreySax'cos 5.0'은 C#과 달리 암시 적 변환 연산자가 없기 때문에 작동하지 않습니다. F # 컴파일러는 '5.0'을 먼저 'Base'로 변환 한 다음'-'를 적용해야한다는 것을 짐작할 수 없습니다. – bytebuster

+0

@bytebuster 나는 이것이 실패한 이유를 안다. 문제는이 코드가 원래의 'cos'정의를 숨기고,'cos 5.0'과 같은 표현식을 만들어 냄으로써 그렇지 않으면 더 이상 컴파일되지 않는다는 것입니다. –

관련 문제