타입 클래스를 제공하거나 인터페이스를 구현할 때의 장점은 해당 typeclass 또는 인터페이스를 사용하도록 작성된 코드가 수정하지 않고 코드를 사용할 수 있다는 것입니다.
어떤 프로그램을 Comonad
으로 쓸 수 있습니까? Comonad
은 extract
을 사용하여 현재 위치의 값을 검사하고 (이웃을 관찰하지 않고) duplicate
또는 extend
으로 모든 위치의 이웃을 관찰하는 방법을 제공합니다. 추가 기능이 없어도이 기능은 대단히 유용하지 않습니다. 그러나 Comonad
인스턴스와 함께 다른 기능이 필요한 경우 로컬 데이터와 다른 곳의 데이터에 의존하는 프로그램을 작성할 수 있습니다. 예를 들어, advance
과 같이 위치를 변경할 수있는 함수가 필요하면 데이터 구조 자체가 아니라 데이터의 로컬 구조에만 의존하는 프로그램을 작성할 수 있습니다.
는 구체적인 예를 들어, Comonad
면 다음과 같은 Bidirectional
클래스로 작성 셀룰러 오토마타 프로그램을 고려 : extract
데이터가 셀에 저장에, Comonad
과 함께,
이
class Bidirectional c where
forward :: c a -> Maybe (c a)
backward :: c a -> Maybe (c a)
프로그램이 사용할 수 및 현재 셀의 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))
임의의 합성 주석에서 작동하도록 매개 변수화 된 모든 기능을 사용할 수 있습니다. 나는 지금 당장은 지적 할 수 없지만, 존재하는 것이 있다고 확신합니다. –
[하스켈의 comonad typeclass 란 무엇입니까?] (http://stackoverflow.com/questions/8428554/what-is-the-comonad-typeclass-in-haskell?rq=1) – sjy