2

型ファミリを使用してコードをリファクタリングします。以下はプロジェクトで使用される型クラスです。

class HeukaryaGene (d :: *) where
  type TypeGeneStr d :: *
  type 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 :: TypeGeneStr d

しかし、ghc は常に多くの同じことを訴えます。

Could not deduce (TypeGeneStr d ~ TypeGeneStr d2)
from the context (HeukaryaGene d)
  bound by the class declaration for `HeukaryaGene'
  at AI/Heukarya/Gene.hs:(22,1)-(42,63)
NB: `TypeGeneStr' is a type function, and may not be injective
The type variable `d2' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Expected type: [TypeGeneStr d]
  Actual type: [TypeGeneStr d2]
In the return type of a call of `lexByArrow'
In the expression: lexByArrow typo
In an equation for `showExpandTypeArgs':
    showExpandTypeArgs dynam
      = lexByArrow typo
      where
          typo = showGeneTypeRep $ geneTypeRep dynam :: TypeGeneStr d

私は何を誤解したのだろうか

4

3 に答える 3

4

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されますが、その型シグネチャを (明らかに) 削除するだけでは問題は解決しません。タイプチェックへ。Typod

(ここで表示されるエラー メッセージは、何が起こっているのか、なぜエラーが発生したのかを明確にしていないため、紛らわしいものです。)


これに対して私が考えることができる最も簡単な修正は、d1 つを取らないすべての関数にダミー パラメーターを含めることです。そのパラメータを分析することはありません。インスタンスの選択を誘導するために使用されます。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

私が行った唯一の変更は、 のタイプ シグネチャの変更typedata削除だけでした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 回しか使用できないことを意味するため、上記のデータ ファミリ バージョンと本質的に同じ欠点があります。dtgstgr


lexByArrow別の解決策は、またはshowGeneTypeRepの外部で呼び出しを行うことがない場合はshowExpandTypeArgs、型クラスからそれらを削除し、ユーザーがshowExpandTypeArgsどのように実装できるようにするかです。geneTypeRepArgsしかし、それは助けにはなりません。


最後の解決策は、型クラスを使用せず、辞書を自分で処理することです。これはかなり抜本的な再設計になります (必ずしも悪いものではありません) が、TypeGeneStrandの型シノニムが本当に必要な場合は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 }

実際、このソリューションでdgeneTypeRep、 、したがってshowExpandTypeArgs、したがってHeukaryaGeneそれ自体が不必要である可能性がありTypeableますd。しかし、それはあなたの特定の状況に依存します。

于 2013-07-20T17:31:21.643 に答える
1

私はこのエレガントな解決策を次のように見つけました:タイプファミリーの理解できないエラーメッセージ

型クラスの定義:

class (
  UnTypeGeneStr (TypeGeneStr d) ~ d, UnTypeGeneRep (TypeGeneRep d) ~ d
  ) => HeukaryaGene d where
  type TypeGeneStr d :: *
  type TypeGeneRep d :: *
  type UnTypeGeneStr o :: *
  type UnTypeGeneRep o :: *

インスタンス定義:

instance HeukaryaGene Dynamic where
  type TypeGeneStr Dynamic = String
  type TypeGeneRep Dynamic = TypeRep
  type UnTypeGeneStr String  = Dynamic
  type UnTypeGeneRep TypeRep = Dynamic

みんなの助けに感謝します!!

于 2013-07-20T18:59:21.340 に答える