2017-03-25 12 views
2

インスタンスが定義されているクラスがあります。フローティング専用のインスタンスを作成する方法は?

data MyValue a = MyValue a 

class TestClass a where 
    funcOne:: (Real b) => a b -> a b 
    funcTwo:: (Real b) => a b -> a b -> a b 


instance TestClass MyValue where 
    funcOne (MyValue x) = MyValue (x*pi) 
    funcTwo (MyValue x) (MyValue y) = MyValue (x*y) 

私は次のエラーを取得する:

Could not deduce (Floating b) arising from a use of `pi' 
     from the context: Real b 

私はエラーを理解し、私は、私はそれを解決する方法を知らない それはこのようになります。

(Real b)(Floating b)に変更することはできません。他のインスタンスもIntegral型で動作するはずです。しかしMyValueFloatingとのみ意味があります。コンパイラにinstance TestClass MyValueFloatingとしか動作しないことを伝えることは可能ですか?

そうでない場合は、x*piの結果を、xのパラメータと同じRealに戻すことはできますか?タイプが例えばIntegralの場合は、MyValueは意味をなさないので、何が起こるかは関係ありません。

答えて

6

これを達成できますが、そのデータ型またはクラスを変更する必要があります。

  • 特にMyValueは、そのコンストラクタにその制約を焼くために、おそらく理にかなっているだけにして、フローティングで理にかなっている場合。

    {-# LANGUAGE GADTs #-} 
    
    data MyValue :: * -> * where 
        MyValue :: Floating a => a -> MyValue a 
    

    これはaが故に

    funcOne (MyValue x) = MyValue $ x*pi 
    

    次に、動作する、実際FloatingインスタンスであることMyValue aを受け入れる任意の機能を保証します。

  • これが含まれているタイプの特定の制約を必要とする共通のテーマであれば、あなたは、代わりに常にRealを必要とする、インスタンス上の制約に依存することができます:

    {-# LANGUAGE TypeFamilies, ConstraintKinds #-} 
    import GHC.Exts (Constraint) 
    
    class TestClass a where 
        type Testable a b :: Constraint 
        type Testable a b = Real b -- default constraint 
        funcOne:: Testable b => a b -> a b 
        funcTwo:: Testable b => a b -> a b -> a b 
    
    instance TestClass MyValue where 
        type Testable MyValue b = Floating b 
        funcOne (MyValue x) = MyValue $ x*pi 
        ... 
    
  • しかし、おそらくそれはですパラメータ化された(* -> *)型をTestClassが扱うようにする正しい決定ではなく、パラメトリシティを再度人為的に制約する必要がある場合には、単に代わりにしないでください

    class TestClass q where 
        funcOne :: q -> q 
        funcTwo :: q -> q -> q 
    
    instance Floating a => TestClass (MyValue a) where 
        funcOne (MyValue x) = MyValue $ x*pi 
        funcTwo (MyValue x) (MyValue y) = MyValue $ x*y 
    

    それはとにかく私にとっては綺麗に見えます。

    class TestClass q where 
        type ToTest q :: * 
        ... 
    
    instance Floating a => TestClass (MyValue a) where 
        type ToTest (MyValue a) = a 
        ... 
    
:メソッドの一部が含まれているタイプにアクセスする必要がない場合は、それが関連付けられているタイプのファミリを使用して、このアプローチでも可能です
2

その1つのインスタンスにはbを限定する方法がありません。 TestClassは、にはfuncOneが有効だと言います。これがMyValueの場合でない場合、MyValueTestClassのインスタンスではありません。さらに、正確性を失うことなくFloatingからRealにキャストする方法はありません。 Realタイプに到達する唯一の方法はfromIntegerですが、タイプを最初にIntegerにキャストすると、小数部が常に失われます。

あなたは常にGADTsを使用してFloating制約持ってbを制限異なるMyValueタイプを使用している(私が知っていること)を行うことができる唯一の事:あなたが値を持つたびに

{-# LANGUAGE GADTs #-} 

data MyValue' = Floating a => MyValue' a 

instance TestClass MyValue' where 
    -- Pattern matching on MyValue' brings the 'Floating' constraint into scope. 
    funcOne (MyValue' x) = MyValue' $ x * pi 
    ... 

今をその値に対するパターン一致がMyValue' aであることは、aFloatingのインスタンスであるという証明です。 TestClassはタイプaの値をとらない機能がある場合は、このアプローチが失敗したことを

注:pureTestbで今

class TestClass a where 
    ... 
    pureTest :: Real b => b -> a b 

Floatingので、pureTest b = MyValue' bである必要はありません型エラーになります。

関連する問題