instance宣言練習
Haskellの型クラスあたりは幾ら説明読んでもよくわからんから試してみた。
二分木(Tree)つくってderivingせずにinstance宣言?練習。
ReadクラスとApplicativeクラスが結構迷った。
Applicativeはとりあえず型エラーは出なくなったが使い方がいまいち分からない。まあTreeには合わないだけかも。
Functorは実装してみたら良く理解出来た。Functorはただの入れ物だね。
Monadも定義は簡単だった。そして前より理解が深まった気がする。
インスタンス宣言の基本は次のようなもの。
instance {TypeClass} {TypeInstance} where method1 = ... method2 = ...
ここの{TypeClass}に型クラスを、{TypeInstance}に型インスタンス?を当てはめる。
で、今回(Tree a)型をインスタンスにするわけだから、
instance {TypeClass} (Tree a) where method1 = ... method2 = ...
こんな感じ。
ここの型変数aにクラス制限?(例としてEq)をつけると、
instance (Eq a) => {TypeClass} (Tree a) where method1 = ... method2 = ...
こうなる。関数との一貫性はあるが、初見ではちょっと分かりにくい。
さらにFunctor, Applicative, Monadとなると
instance Functor Tree where method1 = ... method2 = ...
こう書くらしい。あれ(Tree a)のaはどこいったん?と初め超悩んだ。そういうものらしい。
まあ理解は後で。
しかしHaskell用語が分からなすぎて困るな。
module Main where import Control.Applicative -- Treeの定義 data Tree a = Leaf a | Tree (Tree a) (Tree a) instance (Show a) => Show (Tree a) where -- showは簡単 show (Tree b c) = "<" ++ show b ++ "|" ++ show c ++ ">" show (Leaf a) = show a instance (Read a) => Read (Tree a) where readsPrec _ s = readsTree s -- ??? where readsTree :: (Read a) => ReadS (Tree a) readsTree s = [ (Tree left right, x) | ('<' , t) <- lex' s, (left , u) <- readsTree t, ('|' , v) <- lex' u, (right, w) <- readsTree v, ('>' , x) <- lex' w ] ++ [ (Leaf x, t) | (x , t) <- reads s {- このreadsは何..? -} ] lex' (c:cs) = [(c, cs)] -- Readで定義されているlexでは上手くいかないので自前で簡易定義 -- 同一ならTrue instance (Eq a) => Eq (Tree a) where -- (==), (/=) が定義されているが、どちらか一方を定義すれば十分 Leaf a == Leaf b = a == b Tree a b == Tree c d = a == c && b == d _ == _ = False -- LeafとTreeを比べたら必ずFalse -- Treeの深さで序列をつけてみる instance (Eq a) => Ord (Tree a) where -- compare, min, max, (<), (>), (>=), (<=) が定義されているが、compareを定義すれば十分 a `compare` b = depth a `compare` depth b where -- 深さ定義 depth :: Tree a -> Int depth (Leaf _) = 1 depth (Tree a b) = if depth a > depth b then 1 + depth a else 1 + depth b instance Functor Tree where -- fmapはmapの拡張版のようなもの fmap f (Leaf a) = Leaf (f a) fmap f (Tree a b) = Tree (fmap f a) (fmap f b) instance Applicative Tree where -- とりあえずpure, (<*>)を定義 pure a = Leaf a Leaf f <*> Leaf a = Leaf (f a) -- Leaf同士 Leaf f <*> Tree t1 t2 = Tree (Leaf f <*> t1) (Leaf f <*> t2) -- Leaf と Tree Tree tf tg <*> Leaf a = Tree (tf <*> Leaf a) (tg <*> Leaf a) -- Tree と Leaf Tree f g <*> Tree t1 t2 = Tree (Tree f g <*> t1) (Tree f g <*> t2) -- Tree同士 instance Monad Tree where Leaf a >>= f = f a Tree t1 t2 >>= f = Tree (t1 >>= f) (t2 >>= f) return = Leaf -- 以下テスト main = do -- 準備 tree1 <- return $ Tree (Leaf 1) (Tree (Tree (Leaf 2) (Leaf 3)) (Leaf 4)) tree2 <- return $ Tree (Leaf 1) (Tree (Leaf 2) (Leaf 3)) tree3 <- return $ Tree (Leaf 1) (Leaf 2) -- Show print $ tree1 -- Read print $ (read "<<20|3>|5>" :: Tree Double) -- Eq print $ Tree (Leaf 2) (Leaf 3) /= Tree (Leaf 2) (Tree (Leaf 4) (Leaf 5)) -- Ord print $ Tree (Leaf 2) (Leaf 3) > Tree (Leaf 2) (Tree (Leaf 4) (Leaf 5)) print $ Tree (Leaf 2) (Leaf 3) >= Tree (Leaf 2) (Tree (Leaf 4) (Leaf 5)) print $ Tree (Leaf 2) (Leaf 3) `compare` Tree (Leaf 2) (Tree (Leaf 4) (Leaf 5)) print $ Tree (Leaf 2) (Leaf 3) `max` Tree (Leaf 2) (Tree (Leaf 4) (Leaf 5)) print $ Tree (Leaf 2) (Leaf 3) `min` Tree (Leaf 2) (Tree (Leaf 4) (Leaf 5)) -- Functor print $ fmap (^2) tree1 -- Applicative print $ Tree (Leaf (*2)) (Tree (Tree (Leaf (*3)) (Leaf (*4))) (Leaf (*5))) <*> Leaf 3 print $ Leaf (*2) <*> Tree (Tree (Leaf 3) (Leaf 5)) (Leaf 4) print $ Tree (Leaf (*2)) (Tree (Tree (Leaf (*3)) (Leaf (*4))) (Leaf (*5))) <*> tree1 print $ Tree (Leaf show) (Tree (Tree (Leaf $ (++"hoge").show) (Leaf $ (++"moge").show)) (Leaf $ (++"fuga").show)) <*> tree1 -- Monad print $ tree1 >>= \x -> Tree (Leaf $ x*3) (Leaf $ x*2) >>= \x -> fmap (*x) tree2
クラスにどんなメソッドが定義されているかはghci立ち上げて
:i Ord
などとやってみると分かる。:iは:infoの略だっけ。
Readクラスに関しては以下を参照した。
しかしそのままでは動かないという罠がある。lexの動作のせいと分かったので、上ではlex'を定義している。