読者です 読者をやめる 読者になる 読者になる

きくらげ観察日記

好きなことを、適当に。

Typeableを使ってみる

Haskell

Data.Typeable.Typeableを使うと、TemplateHaskell無しでも非常に一般的な関数を書くことができるようになります。

型クラスData.Typeableのインスタンスは、typeOfという関数を使うことによって型情報を取得することができます。

>>> :m +Data.Typeable
>>> typeOf (3 :: Int)
Int
>>> typeOf 'a'
Char
>>> typeOf (Just "hoge", ())
((Maybe [Char]),())

また、自前の型に対しても、DeriveDataTypeable拡張を有効にすることでTypeableのインスタンスをderiveすることができます(というか、derive以外でのTypeableのインスタンスの定義は現在のGHCでは禁止されています)。

>>> data Hoge = Hoge Int | Fuga String deriving (Eq, Show, Typeable)
>>> typeOf $ Fuga "foo"
Hoge

この型情報はTypeRepという型になっており、これを使うことで型情報の比較ができるようになります。
これがあるとどのように便利かというと、例えば何らかの理由で複数の型を含むことができるような変数を作らなければならなくなった場合、TypeRepを保存しておくことによって元の型情報を保持することができます。

import Data.Typeable
import Unsafe.Coerce

data Value = IntVal Int
           | StrVal String
           | DoubleVal Double
           deriving (Eq, Show)

n :: Int
n = 3

-- 型情報の削除された値
x :: a
x = unsafeCoerce n

-- xの型情報
t :: TypeRep
t = typeOf n

-- 型情報を元に復元する
toValue :: TypeRep -> (forall a. a) -> Value
toValue rep x
  | rep == typeRep (Proxy :: Proxy Int) = IntVal x
  | rep == typeRep (Proxy :: Proxy String) = StrVal x
  | rep == typeRep (Proxy :: Proxy Double) = DoubleVal x

実行結果:

>>> toValue t x
IntVal 3

このように、保存していた型情報tからxの型を復元し、Intに戻すことができました。