Der Vorteil der Bereitstellung einer Typklasse oder der Implementierung einer Schnittstelle besteht darin, dass Code, der für die Verwendung dieser Typklasse oder Schnittstelle geschrieben wurde, Ihren Code ohne Änderungen verwenden kann.
Welche Programme können in Bezug auf Comonad
geschrieben werden? A Comonad
bietet eine Möglichkeit, den Wert an der aktuellen Position (ohne Beobachtung seiner Nachbarn) unter Verwendung von extract
und einer Möglichkeit zur Überwachung der Umgebung jedes Standortes mit duplicate
oder extend
. Ohne zusätzliche Funktionen ist das nicht besonders nützlich. Wenn wir jedoch auch andere Funktionen zusammen mit der Comonad
-Instanz benötigen, können wir Programme schreiben, die sowohl von lokalen Daten als auch von anderen Daten abhängen. Wenn wir beispielsweise Funktionen benötigen, mit denen wir den Standort ändern können, z. B. Ihre advance
, können wir Programme schreiben, die nur von der lokalen Struktur der Daten und nicht von der Datenstruktur selbst abhängen.
Für ein konkretes Beispiel eines zellulären Automaten Programm geschrieben in Bezug auf Comonad
und die folgenden Bidirectional
Klasse:
class Bidirectional c where
forward :: c a -> Maybe (c a)
backward :: c a -> Maybe (c a)
Das Programm könnte dies, zusammen mit Comonad
, zu extract
Daten in einer Zelle gespeichert und Erkunden Sie die Zellen forward
und backward
der aktuellen Zelle. Er kann duplicate
verwenden, um die Nachbarschaft jeder Zelle zu erfassen, und fmap
, um diese Umgebung zu inspizieren. Diese Kombination von fmap f . duplicate
ist extract f
.
Hier ist ein solches Programm. rule'
ist nur für das Beispiel interessant; Es implementiert zellulare Automatenregeln für die Nachbarschaft mit nur den linken und rechten Werten. rule
extrahiert Daten aus der Umgebung, die der Klasse zugewiesen sind, und führt die Regel für jede Umgebung aus. slice
zieht noch größere Nachbarschaften aus, damit wir sie leicht anzeigen können. simulate
führt die Simulation durch und zeigt diese größeren Nachbarschaften für jede Generation an.
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
Dieses Programm könnte mit der folgenden Bidirectional
Comonad
, ein Zipper
auf einer Liste zu arbeiten, wurden bestimmt.
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
Aber auch mit einem CyclicList
Bidirectional
Comonad
arbeiten.
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'
können wir wiederverwenden simulate
entweder mit Datenstruktur.Die CyclicList
hat eine interessantere Ausgabe, weil sie, anstatt in eine Wand zu stoßen, sich zurückzieht, um mit sich selbst zu interagieren.
{-# 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))
Sie können alle Funktionen verwenden, die parametrisiert sind, um auf beliebigen comonads zu arbeiten. Ich kann jetzt nicht auf etwas hinweisen, aber ich bin mir sicher, dass es einige gibt. –
[Was ist die comonad Typklasse in Haskell?] (Http://stackoverflow.com/questions/8428554/what-is-the-comonad-typeclass-in-haskell?rq=1) – sjy