長さ付きリスト
id:msakaiさんが、sized-list の appendで長さ付きのリストを扱う方法を解説してくれている。すばらしい。
しかし型関数なんていうかっちょいいものがあったのかー。型関数の意味とか能力はさっぱりわからんけど、こんな風にかけてしまうとはおどろきである。参考文献をあとで読んでみることにしよう。
で、とりあえず専用の型クラスを使う方法をやってみた。id:msakaiさんのオリジナルのものとは若干違う気がするが。関数名は適当。
{-# OPTIONS -fglasgow-exts -fallow-undecidable-instances #-} data S n data Z data NON data List n a where Nil :: List Z a Cons :: a -> List n a -> List (S n) a instance (Show a) => Show (List n a) where show Nil = "[]" show (Cons x xs) = show x ++ ":" ++ show xs class Append m n l | m n -> l where append :: (List m a) -> (List n a) -> (List l a) instance Append Z n n where append Nil ys = ys instance Append n m o => Append (S n) m (S o) where append (Cons x xs) ys = Cons x (append xs ys) test :: List (S (S (S (S Z)))) Int test = append (Cons 1 $ Cons 2 Nil) (Cons 3 $ Cons 4 Nil) class Inter a b | a -> b where inter :: x -> (List a x) -> (List b x) instance Inter Z Z where inter _ Nil = Nil instance Inter m n => Inter (S m) (S (S n)) where inter a (Cons x xs) = Cons x $ Cons a $ inter a xs x = inter 0 $ Cons 1 $ Cons 2 Nil
interは、要素の間に指定した要素を挟み込む関数のつもり。これだと常に長さが2倍になるので、型はそういうふうに宣言した。変なinterの定義をするとちゃんと怒られる。すばらしい。ところが、
case x of [] -> ... [x] -> ... x:xs -> ...
みたいな場合分けを書こうとして困った。インスタンス宣言がかぶってしまう。
class Inter a b | a -> b where inter :: x -> (List a x) -> (List b x) instance Inter Z Z where inter _ Nil = Nil instance Inter (S Z) (S Z) where inter a (Cons x Nil) = Cons x Nil instance Inter m n => Inter (S m) (S (S n)) where inter a (Cons x xs) = Cons x $ Cons a $ inter a xs
Functional dependencies conflict between instance declarations: instance Inter (S Z) (S Z) -- Defined at /Users/sho/tmp/haskell/append.hs:(27,0)-(28,36) instance (Inter m n) => Inter (S m) (S (S n)) -- Defined at /Users/sho/tmp/haskell/append.hs:(29,0)-(30,53) Failed, modules loaded: none.
これはそのうち考える。
問題その2。この長さ付きリストに対してフィルターすることを考える。リストの長さは実行時までわからないので、フィルタ関数filtの型はNONとかにしたい。NONは長さのよくわかんないリストを表す型で、NONなものに対してなにかの操作をしても常にNONになるようにしたい。とりあえず思いつきで。
class Filter a where filt :: (x -> y) -> List a x -> List NON y instance Filter Z where filt f Nil = Nil instance (Filter n) => Filter (S n) where filt f (Cons x xs) = Cons (f x) $ filt f xs
あたりまえだが怒られる。Nilの型はNONじゃなくてZだからね。はてさてどうしよう。
型に関しての知識がなんにもないから、Haskellの型システムがどんだけ頑張れるかよくわからないんだよねー。