知っています:
a
親切ですK
- kind の唯一の型
K
はA
andですB
Show (Proxy A)
ホールド
Show (Proxy B)
ホールド
であることを証明するには十分Show (Proxy a)
です。しかし、型クラスは、型で使用するために真であることを証明する必要があるという単なる命題ではなく、実装も提供します。実際に使用するには、 の実装がexistsshow (x :: Proxy a)
であることを証明するだけでなく、それがどれであるかを実際に知る必要があります。Show (Proxy a)
Haskell 型の変数 (制約なし) はパラメトリックです。 で完全にポリモーフィックにする方法はなく、 と で異なる動作を提供するためにa
検査することもできます。必要な「異なる動作」は、タイプごとに1つあることがわかっている場合、タイプごとに異なるインスタンスを選択するだけなので、実際にパラメトリックでなくても可能な限り「パラメトリックに近い」ものです。しかし、それはまだ意味の契約を破るものです.a
A
B
test :: forall (a :: K). Proxy a -> String
型クラスの制約により、型から実装への型クラスのマッピングを使用して、呼び出された型に基づいてコードの動作を切り替えることができる限り、制約された型変数でコードをノンパラメトリックにすることができます。だからtest :: forall (a :: K). Show (Proxy a) => Proxy a -> String
うまくいく。(実際の実装に関しては、型を選択できる同じ最終呼び出し元が、使用する関数から生成されたコードa
のインスタンスも提供します)。Show (Proxy a)
あなたのinstance Show (Proxy (a :: K))
アイデアを使うことができます。すべての. _ _a :: K
_ しかし、インスタンス自体にも同じ問題が発生します。インスタンスでの の実装はでパラメトリックであるため、タイプに基づいて異なる文字列を返すためにそれを検査することはできません。show (x :: Proxy a)
a :: K
show
a
Kish
基本的に実行時に型を検査できるようにするために、制約された型変数の非パラメトリック性を利用するdfeuer の答えのようなものを使用できます。Kish a
制約を満たすために渡されるインスタンス ディクショナリは、基本的に、変数に対して選択された型のランタイム レコードですa
(迂回的な方法で)。このアイデアをさらに推し進めると、Typeable
. しかし、.NET でコードをノンパラメトリックにするためには、なんらかの制約が必要ですa
。
A
またはの選択の実行時表現である型を明示的に使用することもできます(実行時に空の値であり、選択のコンパイル時表現のみを提供する とはB
対照的に)、次のようなものです。Proxy
{-# LANGUAGE DataKinds, GADTs, KindSignatures, StandaloneDeriving #-}
data K = A | B
data KProxy (a :: K)
where KA :: KProxy A
KB :: KProxy B
deriving instance Show (KProxy a)
test :: KProxy a -> String
test = show
(ここでは、実装するだけでなくShow (Kproxy a)
、実際に派生させることもできますが、スタンドアロンの派生が必要であることに注意してください)
これは GADTKProxy
を使用test
して でノンパラメトリックになることを可能にし、本質的に dfeuer の回答a
の制約と同じ仕事をしますが、型シグネチャに余分な制約を追加する必要を回避します。Kish
この投稿の以前のバージョンで、 は でtest
「ちょうど」パラメトリックのままでこれを行うことができると述べましたがa
、これは誤りでした。
もちろん、プロキシを渡すには、実際にKA
orを書く必要がありKB
ます。実際にタイプを選択するために記述しなければならなかった場合、それは面倒ではありませんProxy :: Proxy A
(プロキシの場合はよくありますが、通常はあいまいなタイプを修正するためにのみ使用するためです)。しかし、呼び出しの残りの部分と矛盾している場合、コンパイラはあなたをキャッチしますが、シンボルを 1 つだけ記述Proxy
して、コンパイラに正しい意味を推測させることはできません。型クラスでこれに対処できます。
class KProxyable (a :: K)
where kproxy :: KProxy a
instance KProxyable A
where kproxy = KA
instance KProxyable B
where kproxy = KB
次に、の代わりに を使用し、コンパイラに裸の の型を推測させる代わりにKA
を使用できます。愚かな例の時間:Proxy :: Proxy A
kproxy
Proxy
foo :: KProxy a -> KProxy a -> String
foo kx ky = show kx ++ " " ++ show ky
GHCI:
λ foo KA kproxy
"KA KA"
KProxyable
実際にはどこにも制約を設ける必要はないことに注意してください。型が分かるkproxy
ところで使います。ただし、これはまだ「上から入る」必要があります(制約を満たすインスタンス辞書とまったく同じです)。型の関数パラメトリックを独自に関連させる方法はありません。Show (Proxy a)
a :: K
KProxy a
これを機能させるのはコンストラクターと型の間の対応であるため、 empty-at-runtime でできるように、このスタイルで汎用プロキシを作成できるとは思いませんProxy
。ただし、TemplateHaskell は確かにそのようなプロキシ タイプを生成できます。ここではシングルトンの概念が一般的な考え方だと思うので、 https://hackage.haskell.org/package/singletonsでおそらく必要なものが提供されますが、実際にそのパッケージを使用する方法についてはあまり詳しくありません。