TypeGeneStr d
元の型に戻るなど、型ファミリから移動することはできませんd
。GHC が「NB:`TypeGeneStr'
は型関数であり、単射ではない可能性がある」と言うとき、これはあなたに伝えていることです: それを知っているだけでTypeGeneStr d ~ TypeGeneStr d'
は、それを意味するわけではありませんd ~ d'
(~
型の等価性はどこにあるのか)。
したがって、名前に型シノニムしかなく、元の引数をまったく参照しない型クラス関数がある場合、それを呼び出すことはできません。これは、型クラス内のgeneTypeRep
および unimplementableを除くすべての関数を表しますshowExpandTypeArgs
。これが失敗する理由は、使用する型クラスのインスタンスを GHC が判断できないためです。次の例のように、衝突を許可TypeGeneStr
および/または衝突するとどうなるかを考えてみましょう。TypeGeneRep
class HeukaryaGene Int where
type TypeGeneStr Int = String
type TypeGeneRep Int = ()
showGeneTypeRep () = "Int"
class HeukaryaGene Bool where
type TypeGeneStr Bool = String
type TypeGeneRep Bool = ()
showGeneTypeRep () = "Bool"
ではshowGeneTypeRep ()
、"Int"
または"Bool"
? それとも、それはString
? 私が持っている場合はどうなりますか
class HeukaryaGene () where
type TypeGeneStr () = ()
type TypeGeneRep () = ()
showGeneTypeRep () = ()
それから、あまりにもshowGeneTypeRep ()
可能性が()
あります。これはshowExpandTypeArgs
: insideの定義に噛み付いていますがtypo
、GHC はそれを正しく判断できるdynam :: d
ためgeneTypeRep dynam :: TypeGeneRep d
、どちらを選択すればよいかわかりませんshowGeneTypeRep :: TypeGeneRep d -> TypeGeneStr d
。また、あなたの の呼び出しもlexByArrow
同様の問題に苦しんでいます: GHC が知っているのはtypo :: TypeGeneStr d'
、一部d'
の のそれだけであり、どのバージョンのlexByArrow :: TypeGeneStr d' -> [TypeGeneStr d']
を選択するかはわかりません (それは知っていますがTypeGeneRep d ~ TypeGeneRep d'
、それだけでは決定するのに十分ではありません)。
私の GHC (7.4.2) では、型シグネチャの が型クラス ヘッドの と同じではないというエラーも表示d
されますが、その型シグネチャを (明らかに) 削除するだけでは問題は解決しません。タイプチェックへ。Typo
d
(ここで表示されるエラー メッセージは、何が起こっているのか、なぜエラーが発生したのかを明確にしていないため、紛らわしいものです。)
これに対して私が考えることができる最も簡単な修正は、d
1 つを取らないすべての関数にダミー パラメーターを含めることです。そのパラメータを分析することはありません。インスタンスの選択を誘導するために使用されます。undefined :: d
呼び出しサイトでは、 for some 具体的な type を使用できますd
。それは次のようになります。
class HeukaryaGene (d :: *) where
type TypeGeneStr d :: *
type TypeGeneRep d :: *
lexByArrow :: d -> TypeGeneStr d -> [TypeGeneStr d]
geneTypeRep :: d -> TypeGeneRep d
geneTypeRepArgs :: d -> TypeGeneRep d -> [TypeGeneRep d]
showGeneTypeRep :: d -> TypeGeneRep d -> TypeGeneStr d
showExpandTypeArgs :: d -> [TypeGeneStr d]
showExpandTypeArgs dynam = lexByArrow dynam typo
where
typo = showGeneTypeRep dynam $ geneTypeRep dynam
それが気に入らない場合、私が考えられる最も侵襲の少ない修正方法は、型ファミリからデータ ファミリに移行することです。データ ファミリは型ファミリに似ていますが、キーワードを使用し、data
まったく新しいデータ型を定義する点が異なります。これは重要です。通常のデータ構造と同様に、データ ファミリーは生成的であり、したがって単射的です。(データ ファミリのインスタンス化ごとに、まったく新しい型が生成されます。) これは次のようになります。
class HeukaryaGene (d :: *) where
data TypeGeneStr d :: *
data TypeGeneRep d :: *
lexByArrow :: TypeGeneStr d -> [TypeGeneStr d]
geneTypeRep :: d -> TypeGeneRep d
geneTypeRepArgs :: TypeGeneRep d -> [TypeGeneRep d]
showGeneTypeRep :: TypeGeneRep d -> TypeGeneStr d
showExpandTypeArgs :: d -> [TypeGeneStr d]
showExpandTypeArgs dynam = lexByArrow typo
where
typo = showGeneTypeRep $ geneTypeRep dynam
私が行った唯一の変更は、 のタイプ シグネチャの変更type
とdata
削除だけでしたtypo
。このバージョンの問題点は、HeukaryaGene
今すぐインスタンス化するには、次のように記述する必要があることです。
instance HeukaryaGene Int where
-- You can instantiate data families with newtypes, too.
newtype TypeGeneStr Int = TGSInt String
data TypeGeneRep Int = TGRInt
showGeneTypeRep TGRInt = TGSInt "Int"
つまり、多くの (アン) ラッピングを行う必要がある可能性があります。しかし、これはうまくいきます。
ここで機能的な依存関係を使用することもできますが、基本的にはこのソリューションとその欠点を複製することになります。つまり、 、、またはclass HeukaryaGene d tgs tgr | d -> tgs tgr, tgs -> d, tgr -> d
のいずれかを知っていれば、他の 2 つを推測するのに十分です。これは良さそうに見えますが、特定の型は遺伝子文字列または遺伝子表現型として 1 回しか使用できないことを意味するため、上記のデータ ファミリ バージョンと本質的に同じ欠点があります。d
tgs
tgr
lexByArrow
別の解決策は、またはshowGeneTypeRep
の外部で呼び出しを行うことがない場合はshowExpandTypeArgs
、型クラスからそれらを削除し、ユーザーがshowExpandTypeArgs
どのように実装できるようにするかです。geneTypeRepArgs
しかし、それは助けにはなりません。
最後の解決策は、型クラスを使用せず、辞書を自分で処理することです。これはかなり抜本的な再設計になります (必ずしも悪いものではありません) が、TypeGeneStr
andの型シノニムが本当に必要な場合はTypeGeneRep
、それが私が考えることができる唯一の方法です。それは次のようになります。
data HeukaryaGene d tgs tgr =
HeukaryaGene { lexByArrow :: tgs -> [tgs]
, geneTypeRep :: d -> tgr
, geneTypeRepArgs :: tgr -> [tgr]
, showGeneTypeRep :: tgr -> tgs }
showExpandTypeArgs :: d -> [tgs]
showExpandTypeArgs dynam = lexByArrow typo
where typo = showGeneTypeRep $ geneTypeRep dynam
次に、以前に型HeukaryaGene d => t
を持っていた関数は型を持ちHeukaryaGene d tgs tr -> t
ます。-XRecordWildCards
(および as-patternsを使用すると) ここで役立ち、次のように記述できます。
getTypeRepArgs HeukaryaGene{..} = geneTypeRepArgs . geneTypeRep
一部の辞書に の異なる実装を持たせたい場合はshowExpandTypeArgs
、少し異なる構造にする必要があります。
data HeukaryaGene d tgs tgr =
HeukaryaGene { lexByArrow :: tgs -> [tgs]
, geneTypeRep :: d -> tgr
, geneTypeRepArgs :: tgr -> [tgr]
, showGeneTypeRep :: tgr -> tgs
, showExpandTypeArgs :: d -> [tgs] }
showExpandTypeArgsDefault :: HeukaryaGene d tgs tgr -> d -> [tgs]
showExpandTypeArgsDefault HeukaryaGene{..} dynam = lexByArrow typo
where typo = showGeneTypeRep $ geneTypeRep dynam
次に、初期化中に結び目を結びます。
hgInt :: HeukaryaGene Int String ()
hgInt = HeukaryaGene { lexByArrow = words
, geneTypeRep = const ()
, geneTypeRepArgs = const []
, showGeneTypeRep = const "Int"
, showExpandTypeArgs = showExpandTypeArgsDefault hgInt }
実際、このソリューションでd
はgeneTypeRep
、 、したがってshowExpandTypeArgs
、したがってHeukaryaGene
それ自体が不必要である可能性がありTypeable
ますd
。しかし、それはあなたの特定の状況に依存します。