2

次のコードがあるとしましょう。

class C t where
  g :: t

instance C Int where
  g = 42

単純。次のように、Int で関数を定義することもできます。

f1 :: Int -> Int
f1 x = x * x

私は型ファミリを使用してきましたが、特に型ファミリをData.Has使用しているため、IxSet.

しかし、ここでは簡単な例を示します。XInt に似た新しい type を定義したいとしましょう。これを行うことができます:

type family X
type instance X = Int

X次に、次のように関数を定義できます。

f2 :: X -> X
f2 x = x * x + 1

これまでのところ問題はありません。C Xで行ったように、インスタンスを定義してみましょうC Int

instance C X where
  g = 43

おっと、次のエラーが発生しました。

インスタンスの型シノニム ファミリ アプリケーションが正しくありません:X
のインスタンス宣言で'C X'

次に、少し違うことを試してみましょう。

newtype NewX = NewX X

instance C NewX where
  g = 43

ここで、別のエラーが発生しました。つまり、次のとおりです。

(Num NewX)
リテラルから生じるインスタンスはありません'43'

newtypeキーワードにより、前のクラスがどのクラスに属していたかに関する情報も削除されるようです。ただし、newtypeインスタンス定義で型ファミリを使用できないため、回避できないようです。

そうでなければ推論される追加の明示的なインスタンスの言及でインスタンス定義を書き直す必要なしにこれを行うより良い方法はありますか?


背景情報:

これが機能する必要がある理由は次のとおりです。

import Data.Has
import Data.IxSet

data Col1 = Col1; type instance TypeOf Col1 = Text
data Col2 = Col2; type instance TypeOf Col2 = Text

type Row = FieldOf Col1 :&: FieldOf Col2;

instance Indexable Row where
  empty = ixSet [ixFun $ (\x -> [ Col1 ^. x ]) ] -- Maybe add some more indexes later

これは次の場合に失敗します。

インスタンスの型シノニム ファミリ アプリケーションが正しくありません:Row
のインスタンス宣言で'Indexable Row'

を作成Rowするnewtypeと、次のエラーが発生します。

`^' の使用に起因する (Contains (Labelled Col1 Text) Row) のインスタンスはありません。考えられる修正: (Contains (Labelled Col1 Text) Row) のインスタンス宣言を追加します。

これを回避できる唯一の方法は、次のように長い派生句を追加することです。

newtype Row = Row (FieldOf Col1 :&: FieldOf Col2)
  deriving 
  (
    Contains (Labelled Col1 Text), -- Add this for every column
    Contains (Labelled Col2 Text)  -- ...
  )

「typedef」Contains (Labelled x (TypeOf x))して言うHasCol xことを可能にするものでさえ役に立ちます。

4

2 に答える 2

5

次のファイルはここでコンパイルされます。

{-# LANGUAGE GeneralizedNewtypeDeriving, TypeFamilies #-}

class C a where g :: a
type family X
type instance X = Int
newtype NewX = NewX X deriving Num
instance C NewX where g = 43
于 2012-05-08T02:06:28.713 に答える
3

Anewtypeはまさにそれを行います。それは新しい型をtype定義し、a はシノニムを定義します。一連の派生句が気に入らない場合は、基になる型で同型をいつでも使用できます

instance C NewX where
   g = NewX  43

型のシノニムが Instance 宣言とうまく機能しない理由は、関数 (型関数を含む) が一方向にしか機能しないためです。コンストラクターでのみパターン マッチを実行できるため、newtypeランタイム コストなしで新しい型コンストラクターを導入できます。あなたの問題では、なぜですか

newtype Row = Row {runRow :: FieldOf Col1 :&: FieldOf Col2}

instance Indexable Row where
  empty = ixSet [ixFun $ (\x -> [ Col1 ^. (runRow x) ]) ]

GeneralizedNewtypeDeriving一般的には不健全であることに注意してください。使用を避けるべきだという意味ではありませんが、あなたが望んでいることはおそらく不可能であることを意味しています。


編集(質問者):

さらに良いことに、データ型 Row を変更する必要さえありません

newtype Row = Row ( FieldOf Col1 :&: FieldOf Col2 )

instance Indexable Row where
  empty = ixSet [ixFun $ (\(Row x) -> [ Col1 ^. x ]) ]
于 2012-05-08T03:37:57.890 に答える