2

型クラスと関数従属性を使用して、たとえば次のコードで変換できる型関数を取得し、次に示すように別の型クラスで使用しようとし ていますIntCont Int

{-# LANGUAGE KindSignatures, FunctionalDependencies, FlexibleInstances, FlexibleContexts #-}

newtype TestData a b = TestData b
newtype Cont a = Cont a

class TypeConv (repr :: * -> *) a b | repr a -> b where
class Lift repr a where
    liftOp :: (TypeConv repr a a') => a -> repr a'

instance TypeConv (TestData a) Int (Cont Int) where

instance Lift (TestData a) Int where
    liftOp i = TestData (Cont i)

そして、これがghci7.4.2のエラーです

src/Test.hs:13:26:
    Could not deduce (a' ~ Cont Int)
    from the context (Full (TestData a) Int a')
      bound by the type signature for
                 liftOp :: Full (TestData a) Int a' => Int -> TestData a a'
      at src/Test.hs:13:5-32
      a' is a rigid type variable bound by
         the type signature for
           liftOp :: Full (TestData a) Int a' => Int -> TestData a a'
         at src/Test.hs:13:5
    In the return type of a call of `Cont'
    In the first argument of `TestData', namely `(Cont i)'
    In the expression: TestData (Cont i)

TypeConv型クラスに私が読んだ基金があるとすると、「与えられてrepra私たちは推測することができbます」そしてインスタンスを提供しましたInt、なぜghcはそれを推測できないのa' ~ Cont Intですか?

4

2 に答える 2

6

型関数が必要な場合は、型族を使用してください-それが彼らの目的です。型族は簡単で、あなたが期待することをします。

多くの場合、コンパイラが型を推測しなかった理由は、関数(計算ツール)ではなく関数従属性(論理関係)を指定したためです。Fundepsの使用は、直感に反することで有名です。これは、値レベルで関数型プログラミングを実行しながら、型レベルで論理プログラミングを実行しているためです。スイッチ!素敵なタイプファミリー拡張機能を使用して、タイプレベルで関数を使用します。トークンが4つだけの無料のラムダ冷蔵庫マグネットが付属しています(p&pは含まれていません)。


あなたが何を達成しようとしていたかはわかりませんが、ここに例があります-私が間違った方向に向かっている場合は私を訂正してください。あなたは必要になるでしょう

{-# LANGUAGE TypeFamilies #-}

TypeConv次に、型関数であるローカル型の同義語を含むクラスを定義できます。

class Lift a where
    type TypeConv a
    liftOp :: a -> TypeConv a

そして、インスタンスを作成できます

instance Lift Int where
    type TypeConv Int = TestData (Cont Int)
    liftOp i = TestData (Cont i)

ラップしたいだけならCont

instance Lift Integer where
    type TypeConv Integer = Cont Integer
    liftOp i = Cont i

そして、あなたは夢中になることができます

instance Lift Char where
    type TypeConv Char = [String]
    liftOp c = replicate 4 (replicate 5 c)

それはあなたが持つことができます

*Main> liftOp (5::Int)
TestData (Cont 5)

*Main> liftOp (5::Integer)
Cont 5

*Main> liftOp '5'
["55555","55555","55555","55555"]
于 2012-09-25T23:21:57.750 に答える
2

Andrewはfundeps確かに不必要に批判的であり、型関数はより簡単ですが、関数従属性はしばしば追加の柔軟性を提供します。この場合、より長いクラス定義を受け入れる必要があります

{-# LANGUAGE KindSignatures, 
             FunctionalDependencies, 
             FlexibleInstances, 
             FlexibleContexts #-}

newtype TestData a b = TestData b
newtype Cont a = Cont a

class TypeConv (repr :: * -> *) a b | repr a -> b
class TypeConv repr a b => Lift repr a b | repr a -> b where
    liftOp :: a -> repr b

instance TypeConv (TestData a) Int (Cont Int)

instance Lift (TestData a) Int (Cont Int) where
    liftOp i = TestData (Cont i)

もちろん、タイプ関数ベースのアプローチの方が見栄えがよく、おそらく望ましいでしょう。

于 2012-09-26T03:46:42.560 に答える