いいえ、できません。要するに、依存型付けされた関数を作成しようとしているということであり、Haskell は依存型付けされた言語ではありません。値を真の型に持ち上げることはできないためTypeRep
、目的の関数の型を書き留める方法はありません。これをもう少し詳しく説明するために、まず、タイプの言い回しがrandList
実際には意味をなさない理由を示します。次に、やりたいことができない理由を説明します。最後に、実際に何をすべきかについていくつかの考えを簡単に述べます。
存在論
あなたの型シグネチャは、あなたrandList
が意味したいことを意味することはできません。Haskell のすべての型変数は普遍的に量化されていることを思い出すと、次のようになります。
randList :: forall a. Typeable a => [Dynamic] -> IO [a]
したがって、私はそれを好きなように呼び出す権利がrandList dyns :: IO [Int]
あります。一部のだけでなく、すべて の に対して戻り値を提供できなければなりません。これをゲームと考えると、関数自体ではなく、呼び出し元が を選択できるゲームです。あなたが言いたいこと (これは有効な Haskell 構文ではありませんが、存在データ型1を使用して有効な Haskell に変換できます) は、より似たようなものですa
a
a
randList :: [Dynamic] -> (exists a. Typeable a => IO [a])
これは、リストの要素がのインスタンスである何らかのタイプであることを約束しますが、必ずしもそのようなタイプであるとは限りません。しかし、これでも問題が 2 つあります。まず、そのようなリストを作成できたとしても、それで何ができるでしょうか? そして第二に、そもそもそれを構築することさえできないことが判明しました。a
Typeable
存在リストの要素について知っていることは、それらが のインスタンスでTypeable
あるということだけなので、それらで何ができるでしょうか? ドキュメントを見ると、 のインスタンスを取る関数2が2 つしかないことがわかりTypeable
ます。
したがって、リスト内の要素の型について知っていることは、それらに対してtypeOf
andcast
を呼び出すことができるということだけです。私たちはそれらを使って他のことを有益に行うことは決してできないので、私たちの存在は(これも有効な Haskell ではありません)同様に良いかもしれません。
randList :: [Dynamic] -> IO [(TypeRep, forall b. Typeable b => Maybe b)]
これは、リストのすべての要素に適用typeOf
しcast
、結果を保存し、現在は役に立たない存在型の元の値を破棄すると得られるものです。明らかに、TypeRep
このリストの一部は役に立ちません。リストの後半もそうではありません。普遍的に量化された型に戻ったので、 の呼び出し元は、選択した任意の (型付け可能な) に対してa 、 a 、または arandList
を取得するよう要求する権利が再び与えられます。(実際、リストのさまざまな要素をさまざまな型にインスタンス化できるため、以前よりもわずかに強力です。)しかし、変換元の型がわかっている場合を除き、変換元の型を理解することはできません。保持しようとしていた型情報が失われました。Maybe Int
Maybe Bool
Maybe b
b
そして、それらが役に立たないという事実を脇に置いても、ここで目的の存在型を構築することはできません。存在型リスト ( ) を返そうとすると、エラーが発生しますreturn $ dynListToList dl
。どの特定のタイプで呼び出していdynListToList
ますか? 思い出してくださいdynListToList :: forall a. Typeable a => [Dynamic] -> [a]
。したがって、使用するものを選択するrandList
責任があり a
dynListToList
ます。a
しかし、どれを選択すればよいかわかりません。繰り返しますが、それが質問のソースです。したがって、返そうとしている型は指定されていないため、あいまいです。3
依存タイプ
では、この存在を有用 (かつ可能)にするものは何でしょうか? 実際にはもう少し多くの情報があり a
ますTypeRep
。それで、それをパッケージ化できるかもしれません:
randList :: [Dynamic] -> (exists a. Typeable a => IO (TypeRep,[a]))
ただし、これでは十分ではありません。とTypeRep
は[a]
まったくリンクされていません。TypeRep
とをリンクする方法a
です。
基本的に、あなたの目標は次のようなものを書くことです
toType :: TypeRep -> *
ここで*
は、すべてのタイプの種類です。前に種類を見たことがない場合は、それらは型に対するものであり、型は値に対するものです。 *
型の* -> *
分類、引数が 1 つの型コンストラクタの分類など (たとえば、Int :: *
、Maybe :: * -> *
、Either :: * -> * -> *
、およびMaybe Int :: *
.)
これを使用して、次のように記述できます (繰り返しますが、このコードは有効な Haskell ではありません。実際には、Haskell の型システム内でこのようなコードを記述することはできないため、実際には Haskell に似ているだけです):
randList :: [Dynamic] -> (exists (tr :: TypeRep).
Typeable (toType tr) => IO (tr, [toType tr]))
randList dl = do
tr <- randItem $ map dynTypeRep dl
return (tr, dynListToList dl :: [toType tr])
-- In fact, in an ideal world, the `:: [toType tr]` signature would be
-- inferable.
さて、あなたは正しいことを約束しています: リストの要素を分類する型が存在するのではなくTypeRep
、対応する型がリストの要素を分類する型が存在するということです。これさえできれば、準備万端です。しかしtoType :: TypeRep -> *
、Haskell で書くことは完全に不可能toType tr
です。これを行うには、値に依存する型であるため、従属型言語が必要です。
これは何を意味するのでしょうか?Haskell では、値が他の値に依存することは完全に許容されます。これが関数です。head "abc"
たとえば、値は値に依存します"abc"
。同様に、型コンストラクターがあるため、型が他の型に依存することは許容されます。を考慮Maybe Int
し、それがどのように依存するかInt
。型に依存する値を持つことさえできます! を検討してくださいid :: a -> a
。id_Int :: Int -> Int
これは、 、 などの関数のファミリーですid_Bool :: Bool -> Bool
。どの関数を使用するかは、 のタイプによって異なりa
ます。(本当に、id = \(a :: *) (x :: a) -> x
; Haskell でこれを書くことはできませんが、できる言語はあります。)
ただし、重要なのは、値に依存する型を持つことはできません。次のようなものが必要になるかもしれません:Vec 7 Int
整数の長さ 7 のリストの型である Imagine 。ここで、Vec :: Nat -> * -> *
: 最初の引数が type の値でなければならない型Nat
。しかし、Haskell ではこのようなことは書けません。4 これをサポートする言語は、依存型付けと呼ばれます (そして、上記のように書くid
ことができます)。例にはCoqとAgdaが含まれます。(そのような言語はしばしば証明アシスタントとしても機能し、実際のコードを書くのではなく、一般的に研究作業に使用されます。依存型は難しく、日常のプログラミングに役立つようにすることは活発な研究分野です。)
したがって、Haskell では、最初に型に関するすべてをチェックし、そのすべての情報を破棄してから、値のみを参照するものをコンパイルできます。実際、これはまさに GHC が行っていることです。Haskell では実行時に型をチェックできないため、GHC はプログラムの実行時の動作を変更することなく、コンパイル時にすべての型を消去します。これが、(操作上) 実装が簡単で、完全に安全ではない理由unsafeCoerce
です。実行時には何もしませんが、型システムに問題があります。したがって、toType
Haskell 型システムでのようなものを実装することは完全に不可能です。
実際、お気づきのように、目的の型を書き留めて使用することさえできませんunsafeCoerce
。いくつかの問題については、これで解決できます。関数の型を書き留めることはできますが、チートによって実装するだけです。それがまさにそのfromDynamic
仕組みです。しかし、上で見たように、Haskell 内からこの問題に与える適切な型さえありません。虚数toType
関数を使用すると、プログラムに型を与えることができますが、 の型を書き留めることさえできませんtoType
!
今何?
だから、あなたはこれを行うことはできません。あなたは何をすべきですか?私の推測では、あなたの全体的なアーキテクチャは Haskell には理想的ではありませんが、私はそれを見たことがありません。実際、Haskell プログラムではあまり表示されませんTypeable
。Dynamic
(おそらく、彼らが言うように、「Python のアクセントで Haskell を話している」のかもしれません。) 処理するデータ型のセットが限られている場合は、代わりに単純な古い代数データ型にまとめることができる場合があります。
data MyType = MTInt Int | MTBool Bool | MTString String
次に、 を書き、 , またはisMTInt
を使用します。filter isMTInt
filter (isSameMTAs randomMT)
それが何であるかはわかりませんが、おそらくこの問題を解決する方法があるでしょう。 unsafeCoerce
しかし、率直に言って、自分が何をしているのかを本当に、本当に、本当に、本当に、本当に、本当に理解していない限り、それは良い考えではありません. それでも、おそらくそうではありません。が必要な場合unsafeCoerce
は、便利なだけではないことがわかります。
私はDaniel Wagner のコメントに本当に同意します。おそらく、アプローチを最初から考え直したくなるでしょう。繰り返しになりますが、私はあなたのアーキテクチャを見たことがないので、それが何を意味するのかはわかりません。具体的な問題を抽出できれば、スタック オーバーフローに関する別の質問があるかもしれません。
1次のようになります。
{-# LANGUAGE ExistentialQuantification #-}
data TypeableList = forall a. Typeable a => TypeableList [a]
randList :: [Dynamic] -> IO TypeableList
ただし、このコードはいずれもコンパイルされないため、 で記述したexists
方がわかりやすいと思います。
2技術的には、 や など、関連すると思われる他の関数がいくつかありtoDyn :: Typeable a => a -> Dynamic
ますfromDyn :: Typeable a => Dynamic -> a -> a
。しかし、Dynamic
多かれ少なかれ s の周りに存在するラッパーであり、 and sTypeable
に依存していつ実行するかを判断します (GHC はいくつかの実装固有の型 and を使用しますが、 /の可能性のある例外を除いて、この方法で実行できます)。新しいことをする。そして、タイプの引数を実際には期待していません; それはただのラッパーです。これらの関数、および他の同様の関数は、 と だけでは利用できない追加の機能を提供しません。(たとえば、typeOf
TypeRep
unsafeCoerce
unsafeCoerce
dynApply
dynApp
toDyn
fromDyn
a
cast
typeOf
cast
Dynamic
あなたの問題にはあまり役に立ちません!)
3エラーの動作を確認するには、次の完全な Haskell プログラムをコンパイルしてみてください。
{-# LANGUAGE ExistentialQuantification #-}
import Data.Dynamic
import Data.Typeable
import Data.Maybe
randItem :: [a] -> IO a
randItem = return . head -- Good enough for a short and non-compiling example
dynListToList :: Typeable a => [Dynamic] -> [a]
dynListToList = mapMaybe fromDynamic
data TypeableList = forall a. Typeable a => TypeableList [a]
randList :: [Dynamic] -> IO TypeableList
randList dl = do
tr <- randItem $ map dynTypeRep dl
return . TypeableList $ dynListToList dl -- Error! Ambiguous type variable.
案の定、これをコンパイルしようとすると、次のエラーが発生します。
SO12273982.hs:17:27:
Ambiguous type variable `a0' in the constraint:
(Typeable a0) arising from a use of `dynListToList'
Probable fix: add a type signature that fixes these type variable(s)
In the second argument of `($)', namely `dynListToList dl'
In a stmt of a 'do' block: return . TypeableList $ dynListToList dl
In the expression:
do { tr <- randItem $ map dynTypeRep dl;
return . TypeableList $ dynListToList dl }
しかし、質問の要点と同様に、必要な型がわからないため、「これらの型変数を修正する型シグネチャを追加する」ことはできません。
4ほとんど。GHC 7.4 は、型をカインドにリフティングし、カインド ポリモーフィズムをサポートしています。GHC 7.4 ユーザーマニュアル のセクション 7.8「種類の多型と昇格」を参照してください。これは Haskell を依存型付けするわけではありません — TypeRep -> *
example のようなものはまだ5ではありません — しかし、値のように見えるVec
非常に表現力豊かな型を使って書くことができます。
5技術的には、目的のタイプを持っているように見えるものを書き留めることができますtype family ToType :: TypeRep -> *
。ただし、これはtypeの値ではなく、昇格したkindの 型を取ります。その上、まだそれを実装することはできません。(少なくとも私はそうは思いませんし、あなたがどう思うかはわかりませんが、私はこの分野の専門家ではありません。) しかし、現時点では、私たちはかなりかけ離れています。TypeRep
TypeRep