ランタイムのチェックを一度で済ます
http://d.hatena.ne.jp/Lost_dog/20111128/1322504143
reify_integer がおもしろいなーと思った.
これと同じ雰囲気で, dependent type の例によく出てくる「長さ n のベクトル型 Vec n a」についてこんなのが書けそう.
同じ長さ n のベクトル同士だけ zipWith.長さが異なるベクトル同士だと型が合わずにコンパイルエラー.長さが等しいことは sameLength でランタイムにチェックすることができて,一度チェックすればそれ以降は型で保証される.
{-# LANGUAGE Rank2Types, GADTs, EmptyDataDecls #-} import Prelude hiding (zipWith, foldl) import Control.Applicative ((<$>)) data Z data S n data Vec n a where Nil :: Vec Z a (:::) :: a -> Vec n a -> Vec (S n) a toList :: Vec n a -> [a] toList Nil = [] toList (x ::: xs) = x : toList xs fromList :: [a] -> (forall n. Vec n a -> b) -> b fromList [] k = k Nil fromList (x:xs) k = fromList xs $ \ys -> k (x ::: ys) sameLength :: Vec n a -> Vec m b -> Maybe (Vec n b) sameLength Nil Nil = Just Nil sameLength (_ ::: xs) (y ::: ys) = (y :::) <$> sameLength xs ys sameLength _ _ = Nothing zipWith :: (a -> b -> c) -> Vec n a -> Vec n b -> Vec n c zipWith _ Nil Nil = Nil zipWith f (x ::: xs) (y ::: ys) = f x y ::: zipWith f xs ys zipWith _ _ _ = error "should not happen!" foldl :: (a -> b -> a) -> a -> Vec n b -> a foldl _ z Nil = z foldl f z (x ::: xs) = foldl f (f z x) xs innerProd :: Num a => Vec n a -> Vec n a -> a innerProd xs ys = foldl (+) 0 $ zipWith (*) xs ys main :: IO () main = do as <- readLn :: IO [Int] bs <- readLn :: IO [Int] fromList as $ \xs -> fromList bs $ \ys -> -- ここで zipWith (,) xs ys とは書けない case sameLength xs ys of Just ys' -> do putStr "zip: " >> print (toList $ zipWith (,) xs ys') putStr "innerProd: " >> print (innerProd xs ys') Nothing -> putStrLn $ "different length..."
実行例
% ./Vec [1,2,3] [4,5,6] zip: [(1,4),(2,5),(3,6)] innerProd: 32 % ./Vec [1,2] [3,4,5] different length...