提供類型類或實現接口的優點是,爲了使用該類型類型或接口而編寫的代碼可以在不做任何修改的情況下使用您的代碼。
根據Comonad
可以編寫什麼程序?一個Comonad
提供了一種既檢查在當前位置的值使用extract
和方式與duplicate
或extend
觀察每個位置附近(不遵守它的鄰居)。沒有任何額外的功能,這不是非常有用。但是,如果我們還需要其他函數以及Comonad
實例,我們可以編寫依賴於本地數據和來自其他地方的數據的程序。例如,如果我們需要允許我們更改位置的函數(例如advance
),那麼我們可以編寫僅依賴於數據本地結構的程序,而不是數據結構本身。
對於一個具體的例子,考慮寫在Comonad
術語和下述Bidirectional
類元胞自動機程序:
class Bidirectional c where
forward :: c a -> Maybe (c a)
backward :: c a -> Maybe (c a)
這一方案可以利用這一點,連同Comonad
,對存儲在單元extract
數據和探索當前單元格的單元格forward
和backward
。它可以使用duplicate
來捕獲每個單元格的鄰域,並使用fmap
來檢查該鄰域。 fmap f . duplicate
的這個組合是extract f
。
這是一個這樣的程序。 rule'
只是一個有趣的例子;它僅使用左值和右值在鄰域上實現細胞自動機規則。 rule
從給定的類中提取鄰居的數據,並在每個鄰域運行規則。 slice
拉出更大的街區,以便我們可以輕鬆地展示它們。 simulate
運行模擬,爲每一代顯示這些較大的鄰域。
rule' :: Word8 -> Bool -> Bool -> Bool -> Bool
rule' x l m r = testBit x ((if l then 4 else 0) .|. (if m then 2 else 0) .|. (if r then 1 else 0))
rule :: (Comonad w, Bidirectional w) => Word8 -> w Bool -> w Bool
rule x = extend go
where
go w = rule' x (maybe False extract . backward $ w) (extract w) (maybe False extract . forward $ w)
slice :: (Comonad w, Bidirectional w) => Int -> Int -> a -> w a -> [a]
slice l r a w = sliceL l w (extract w : sliceR r w)
where
sliceR r w | r > 0 = case (forward w) of
Nothing -> take r (repeat a)
Just w' -> extract w' : sliceR (r-1) w'
sliceR _ _ = []
sliceL l w r | l > 0 = case (backward w) of
Nothing -> take l (repeat a) ++ r
Just w' -> sliceL (l-1) w' (extract w':r)
sliceL _ _ r = r
simulate :: (Comonad w, Bidirectional w) => (w Bool -> w Bool) -> Int -> Int -> Int -> w Bool -> IO()
simulate f l r x w = mapM_ putStrLn . map (map (\x -> if x then '1' else '0') . slice l r False) . take x . iterate f $ w
這一計劃可能已經打算用下面的Bidirectional
Comonad
,名單上的一個Zipper
工作。
data Zipper a = Zipper {
heads :: [a],
here :: a,
tail :: [a]
} deriving Functor
instance Bidirectional Zipper where
forward (Zipper _ _ [] ) = Nothing
forward (Zipper l h (r:rs)) = Just $ Zipper (h:l) r rs
backward (Zipper [] _ _) = Nothing
backward (Zipper (l:ls) h r) = Just $ Zipper ls l (h:r)
instance Comonad Zipper where
extract = here
duplicate (Zipper l h r) = Zipper (goL (h:r) l) (Zipper l h r) (goR (h:l) r)
where
goL r [] = []
goL r (h:l) = Zipper l h r : goL (h:r) l
goR l [] = []
goR l (h:r) = Zipper l h r : goR (h:l) r
但也將與CyclicList
Bidirectional
Comonad
工作。
data CyclicList a = CL a (Seq a)
deriving (Show, Eq, Functor)
instance Bidirectional CyclicList where
forward (CL x xs) = Just $ case viewl xs of
EmptyL -> CL x xs
x' :< xs' -> CL x' (xs' |> x)
backward (CL x xs) = Just $ case viewr xs of
EmptyR -> CL x xs
xs' :> x' -> CL x' (x <| xs')
instance Comonad CyclicList where
extract (CL x _) = x
duplicate (CL x xs) = CL (CL x xs) (go (singleton x) xs)
where
go old new = case viewl new of
EmptyL -> empty
x' :< xs' -> CL x' (xs' >< old) <| go (old |> x') xs'
我們可以重新使用simulate
與任一數據結構。CyclicList
有一個更有趣的輸出,因爲它不會撞到牆上,而是繞回來與自己進行交互。
{-# LANGUAGE DeriveFunctor #-}
import Control.Comonad
import Data.Sequence hiding (take)
import Data.Bits
import Data.Word
main = do
putStrLn "10 + 1 + 10 Zipper"
simulate (rule 110) 10 10 30 $ Zipper (take 10 . repeat $ False) True (take 10 . repeat $ False)
putStrLn "10 + 1 + 10 Cyclic"
simulate (rule 110) 10 10 30 $ CL True (fromList (take 20 . repeat $ False))
您將能夠使用參數化的任何函數在任意連接器上工作。我現在不能指出任何權利,但我確信有一些存在。 – 2014-09-04 23:38:03
[什麼是Haskell中的comonad類型類?](http://stackoverflow.com/questions/8428554/what-is-the-comonad-typeclass-in-haskell?rq=1) – sjy 2014-09-05 07:31:41