(以下では、 と を単純化Show
しRead
ます。
class Show a where show :: a -> String
class Read a where read :: String -> a
read
そして、決して失敗しないと仮定してください。)
フォームの存在型を作成できることはよく知られています
data ShowVal where
ShowVal :: forall a. Show a => a -> ShowVal
そして、次の:: [ShowVal]
ような「異種リスト」を作成します
l = [ShowVal 4, ShowVal 'Q', ShowVal True]
これは比較的役に立たないこともよく知られています:: [String]
.
l = [show 4, show 'Q', show True]
これはまさに同形です (結局のところ、a でできること
ShowVal
はshow
それだけです)。
リスト内の各値に対して、 の結果がshow
自動的にメモ化されるため、遅延性が特に優れています。したがって、 noString
は複数回計算されます (String
使用されていない s はまったく計算されません)。
Aは、関数が辞書でShowVal
ある存在タプルと同等です。exists a. (a -> String, a)
Show
についても同様の構成を作成できますRead
。
data ReadVal where
ReadVal :: (forall a. Read a => a) -> ReadVal
read
は戻り値がポリモーフィックであるため、存在的ではなく普遍的であることに注意してくださいReadVal
(つまり、Haskell にはファーストクラスの普遍性があるため、実際にはまったく必要ないということです。ただし、ここではそれを使用して、Show
)。
リストを作成することもできます:: [ReadVal]
:
l = [ReadVal (read "4"), ReadVal (read "'Q'"), ReadVal (read "True")]
と同様にShow
、リストは次のよう:: [ReadVal]
にリストに同形です。:: [String]
l = ["4", "'Q'", "True"]
(いつでもオリジナルString
を取り戻すことができます
newtype Foo = Foo String
instance Read Foo where read = Foo
Read
型クラスが開いているためです。)
Aはユニバーサル関数
(CPS スタイルの表現)ReadVal
と同等です。forall a. (String -> a) -> a
ここでは、戻り値は引数ではなく多態的であるため、Read
ディクショナリはReadVal
プロデューサーではなくユーザーによって提供されます。
String
ただし、これらの表現のどちらでも、 を使用した表現で得られる自動メモ化は得られませんShow
。私たちの型は高価な操作なので、同じ型に対して同じものを複数回read
計算したくないとしましょう
。String
閉じた型があれば、次のようなことができます。
data ReadVal = ReadVal { asInt :: Int, asChar :: Char, asBool :: Bool }
そして、値を使用します
ReadVal { asInt = read s, asChar = read s, asBool = read s }
または、それらの線に沿った何か。
しかし、この場合、ReadVal
を 1 つの型として
のみ使用したとしてString
も、値が使用されるたびに が解析されます。多態性を保ちながらメモ化する簡単な方法はありReadVal
ますか?
(この場合と同様に、GHC に自動的に実行させるのShow
が理想的です。何らかの方法で可能であれば、より明示的なメモ化アプローチ (おそらくTypeable
制約を追加することによるものでしょうか?) も問題ありません。)