Arrows
được tổng quát theo Danh mục và do đó, theo kiểu số Category
.
class Category f where
(.) :: f a b -> f b c -> f a c
id :: f a a
Arrow
định nghĩa typeclass có Category
làm lớp cha. Các thể loại (trong ý nghĩa haskell) tổng quát hóa các hàm (bạn có thể soạn chúng nhưng không áp dụng chúng) và do đó chắc chắn là một "mô hình tính toán". Arrow
cung cấp Category
với cấu trúc bổ sung để làm việc với bộ dữ liệu. Vì vậy, trong khi Category
phản ánh điều gì đó về không gian chức năng của Haskell, Arrow
mở rộng điều gì đó về loại sản phẩm.
Mỗi Monad
phát sinh cái gì đó gọi là "Danh mục Kleisli" và cấu trúc này cung cấp cho bạn các phiên bản ArrowApply
. Bạn có thể tạo một Monad
trong số bất kỳ ArrowApply
nào sao cho toàn bộ vòng tròn không thay đổi hành vi của bạn, do đó, theo một số ý nghĩa sâu, Monad
và ArrowApply
là giống nhau.
newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }
instance Monad m => Category (Kleisli m) where
id = Kleisli return
(Kleisli f) . (Kleisli g) = Kleisli (\b -> g b >>= f)
instance Monad m => Arrow (Kleisli m) where
arr f = Kleisli (return . f)
first (Kleisli f) = Kleisli (\ ~(b,d) -> f b >>= \c -> return (c,d))
second (Kleisli f) = Kleisli (\ ~(d,b) -> f b >>= \c -> return (d,c))
Trên thực tế mỗi Arrow
làm phát sinh một Applicative
(phổ định lượng để có được những loại phải) ngoài các Category
lớp cha, và tôi tin rằng sự kết hợp của các hợp Category
và Applicative
là đủ để tái tạo lại Arrow
của bạn.
Vì vậy, các cấu trúc này được kết nối sâu sắc.
Cảnh báo: bình luận mơ hồ trước. Một sai số trung tâm giữa Functor
/Applicative
/Monad
cách suy nghĩ và Category
/Arrow
cách suy nghĩ là trong khi Functor
và ilk của nó là khái quát ở mức độ đối tượng (loại trong Haskell), Category
/Arrow
là generelazation của khái niệm về morphism (các chức năng trong Haskell). Niềm tin của tôi là suy nghĩ ở cấp độ tổng quát hình thái liên quan đến mức độ trừu tượng cao hơn so với suy nghĩ ở cấp độ tổng quát đối tượng. Đôi khi đó là một điều tốt, những lúc khác thì không. Mặt khác, mặc dù thực tế rằng Arrows
có cơ sở phân loại và không có ai trong toán học nghĩ rằng Applicative
thú vị, đó là sự hiểu biết của tôi rằng Applicative
thường được hiểu rõ hơn Arrow
.
Về cơ bản bạn có thể nghĩ đến "Thể loại < mũi tên < ArrowApply" và "functor < applicative < Monad" như vậy mà "Thể loại ~ functor", "mũi tên ~ applicative" và "ArrowApply ~ Monad".
More bê tông Dưới: Đối với các cấu trúc khác để mô hình tính toán: người ta thường có thể đảo ngược sự chỉ đạo của "mũi tên" (chỉ có nghĩa morphisms đây) trong công trình xây dựng phân loại để có được những "kép" hay "đồng xây dựng ". Vì vậy, nếu một đơn nguyên được định nghĩa là
class Functor m => Monad m where
return :: a -> m a
join :: m (m a) -> m a
(okay, tôi biết rằng không phải là cách Haskell định nghĩa sự vật, nhưng ma >>= f = join $ fmap f ma
và join x = x >>= id
nên nó chỉ cũng có thể là) thì comonad là
class Functor m => Comonad m where
extract :: m a -> a -- this is co-return
duplicate :: m a -> m (m a) -- this is co-join
Điều này hóa ra lại khá phổ biến. Nó chỉ ra rằng Comonad
là cấu trúc cơ bản cơ bản của automata di động. Đối với completness, tôi phải chỉ ra rằng Edward Kmett của Control.Comonad
puts duplicate
trong một lớp học giữa functor và Comonad
cho "Nối Dài functors" bởi vì bạn cũng có thể định nghĩa
extend :: (m a -> b) -> m a -> m b -- Looks familiar? this is just the dual of >>=
extend f = fmap f . duplicate
--this is enough
duplicate = extend id
Nó chỉ ra rằng tất cả Monad
s cũng là "Nối Dài"
monadDuplicate :: Monad m => m a -> m (m a)
monadDuplicate = return
trong khi tất cả Comonads
là "Joinable"
comonadJoin :: Comonad m => m (m a) -> m a
comonadJoin = extract
nên những cấu trúc này rất gần nhau.
Trang Monad vs. Arrows liên kết với một bài báo cũng so sánh các functors ứng dụng (còn gọi là thành ngữ). –
Functors ứng dụng chắc chắn nhất * là * tốt ở tính toán composable! Trong thực tế, họ sáng tác tốt hơn so với monads (thành phần của hai functors ứng dụng là một functor ứng dụng, mà không giữ cho monads). – ehird