Haskell GADTで存在型から型を復元

GADTは証拠みたいなもの(というか証明)なので この証拠さえあれば 型を存在限量で隠蔽しても パターンマッチングで復元できてしまうのであった。

コード

{-# LANGUAGE GADTs #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE KindSignatures #-}

-- 証拠
data Wit :: * -> * where
  IntWit :: Wit Int -- Int型である
  BoolWit :: Wit Bool -- Bool型である
  StringWit :: Wit String -- String型である
  ShowWit :: Show a => Wit a -- Showクラスに属する何かである…!

-- 証拠つきの存在型
data Ex = forall a. Ex (Wit a) a

-- 適当なデータ型 (Showクラスに属する)
data MyData = MyData deriving Show


heterolist :: [Ex]
heterolist = [Ex IntWit 1, Ex BoolWit True, Ex StringWit "abc", Ex ShowWit MyData]

--  GADTが証拠として機能するため, 個々の型のshowが使える
instance Show Ex where
  show (Ex IntWit i)      = show i ++ "::Int"
  show (Ex BoolWit b)     = show b ++ "::Bool"
  show (Ex StringWit str) = show str ++ "::String"
  show (Ex ShowWit a)     = show a ++"::Show a=>a"

テスト

*Main> heterolist
[1::Int,True::Bool,"abc"::String,MyData::Show a=>a]

rants

こーゆーのは Coqでは普通にinductive predicateとかで使うけど、Haskellの型クラスと組み合わせることができて驚き。

もういっちょ

こういうのは良いかも

-- import Data.Maybe (mapMaybe)
fromInt (Ex IntWit i) = Just i
fromInt _             = Nothing

extractInt = mapMaybe fromInt

ここで mapMaybe :: (a -> Maybe b) -> [a] -> [b]

*Main> :t fromInt
fromInt :: Ex -> Maybe Int

*Main> :t extractInt
extractInt :: [Ex] -> [Int]

*Main> extractInt heterolist
[1]

あとはfromInt的な関数をいちいち定義せずに済めばよいのだけど。パターンマッチが失敗したらNothingを返すのはdo記法だけ?