3

次のコードを検討してください。

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

最後の行でエラーが発生します。

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

と同じcos aInherited:同じエラーが発生しますが-(aInherited :> Base)cos (aInherited :> Base)機能します。

エラー メッセージは、これらの関数が戻り値の型-またはcos引数の型と同じであることを要求していることを示しています。これは非常に厳しい要件のようです。

  • 演算子を定義する基本型から継承するクラスの場合、すべての演算子を再定義しない限り、これは不可能です。
  • これらのクラスが制御できない外部ライブラリに存在する場合、オプションはさらに制限されます。

これを回避する方法はありますか?F# ソース コードでは、cos関数は で定義されていprim-types.fsます。

4

3 に答える 3

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;}
于 2013-02-23T18:12:46.007 に答える
2

さらに面白くなります。継承された型の演算子を再定義し、それらを使用して基本クラスの演算子を呼び出すという、迅速でハックなソリューションを使用できると思いましたが、継承された型で演算子を定義した後でも、エラーメッセージが表示されます最後のケース (これは本当に奇妙です)。

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

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

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

元の定義の代わりにこの定義を使用する場合少なくとも演算子を使用できるようにする必要がありますが、代わりに「ベース」のインスタンスを期待するという同じエラーメッセージが表示されます (これは非常に奇妙です)。

私の推測では、コンパイラのバグ、または少なくとも言語仕様のエッジ ケースを発見したと思います。次のバージョンでこれを修正できるように、これをfsbugsat に電子メールで送信する必要があります。microsoft.com

于 2013-02-23T14:57:11.603 に答える
2

演算子をモジュールに入れることができます:

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

open組み込み演算子をシャドウするモジュール。そうすれば、予想される署名に制約されなくなります (関連する潜在的なバグも回避されます)。これは、コア ライブラリでチェック オペレータに使用されるのと同じ手法です。

open BaseOps

let aInherited = Inherited(5.0)
cos aInherited // OK
-aInherited    // OK
于 2013-02-23T14:59:07.400 に答える