Tôi muốn đề xuất một cách tiếp cận có hệ thống hơn để trả lời câu hỏi này và cũng để hiển thị các ví dụ không sử dụng bất kỳ thủ thuật đặc biệt nào như các giá trị "dưới cùng" hoặc các loại dữ liệu vô hạn hoặc bất cứ điều gì tương tự.
Khi nào các hàm tạo kiểu không có các thể hiện lớp kiểu?
Nói chung, có hai lý do tại sao một hàm tạo kiểu có thể không có một thể hiện của một lớp loại nhất định:
- Không thể thực hiện chữ ký loại của các phương thức cần thiết từ lớp loại.
- Có thể thực hiện các chữ ký loại nhưng không thể đáp ứng các luật yêu cầu.
Ví dụ về loại thứ nhất dễ hơn loại thứ hai vì đối với loại thứ nhất, chúng ta chỉ cần kiểm tra xem người ta có thể thực hiện chức năng với chữ ký loại đã cho hay không, trong khi đối với loại thứ hai, chúng tôi được yêu cầu chứng minh rằng không thực hiện có thể có thể đáp ứng các luật.
Ví dụ cụ thể
Đây là một contrafunctor, không phải functor, liên quan đến tham số loại a
, bởi vì a
ở vị trí chống chỉ định. Không thể thực hiện một chức năng với chữ ký loại (a -> b) -> F z a -> F z b
.
Một hàm tạo kiểu không phải là hàm functor hợp pháp mặc dù chữ ký kiểu của fmap
có thể được thực thi:
data Q a = Q(a -> Int, a)
fmap :: (a -> b) -> Q a -> Q b
fmap f (Q(g, x)) = Q(\_ -> g x, f x) -- this fails the functor laws!
Khía cạnh tò mò của ví dụ này là chúng ta có thể thực hiện fmap
đúng loại mặc dù F
không thể là functor vì nó sử dụng a
ở vị trí chống chỉ định. Vì vậy, việc triển khai fmap
hiển thị ở trên là sai lệch - mặc dù nó có chữ ký loại chính xác (tôi tin rằng đây là cách thực hiện duy nhất có thể của chữ ký loại đó), luật functor không được thỏa mãn. Ví dụ, fmap id
≠ id
, vì let (Q(f,_)) = fmap id (Q(read,"123")) in f "456"
là 123
, nhưng let (Q(f,_)) = id (Q(read,"123")) in f "456"
là 456
.
Trong thực tế, F
chỉ là một profunctor, - nó không phải là functor hay contrafunctor.
Một functor hợp pháp không áp dụng vì chữ ký loại pure
không thể được thực hiện: lấy đơn vị Nhà văn (a, w)
và loại bỏ các ràng buộc w
phải là một đơn sắc. Sau đó, không thể xây dựng một giá trị loại (a, w)
ra a
.
Một functor không áp dụng vì chữ ký loại <*>
không thể được thực hiện : data F a = Either (Int -> a) (String -> a)
.
Một functor không áp dụng hợp pháp mặc dù các phương thức lớp loại có thể được thực hiện:
data P a = P ((a -> Int) -> Maybe a)
Trình xây dựng kiểu P
là một functor vì nó a
chỉ sử dụng trong các vị trí covariant.
instance Functor P where
fmap :: (a -> b) -> P a -> P b
fmap fab (P pa) = P (\q -> fmap fab $ pa (q . fab))
Việc thực hiện duy nhất có thể của chữ ký loại <*>
là một hàm luôn trả về Nothing
:
(<*>) :: P (a -> b) -> P a -> P b
(P pfab) <*> (P pa) = \_ -> Nothing -- fails the laws!
Nhưng việc thực hiện này không thỏa mãn luật định danh cho các chức năng ứng dụng.
- Một functor
Applicative
nhưng không phải làMonad
vì chữ ký loại bind
không thể được thực hiện.
Tôi không biết bất kỳ ví dụ như vậy!
- Một functor
Applicative
nhưng không phải làMonad
vì luật pháp không thể được thỏa mãn mặc dù chữ ký loại bind
có thể được thực thi.
Ví dụ này đã tạo ra khá nhiều cuộc thảo luận, vì vậy có thể nói rằng việc chứng minh ví dụ này đúng là không dễ dàng. Nhưng một số người đã xác minh điều này một cách độc lập bằng các phương pháp khác nhau. Xem Is `data PoE a = Empty | Cặp aa` một đơn nguyên? để thảo luận thêm.
data B a = Maybe (a, a)
deriving Functor
instance Applicative B where
pure x = Just (x, x)
b1 <*> b2 = case (b1, b2) of
(Just (x1, y1), Just (x2, y2)) -> Just((x1, x2), (y1, y2))
_ -> Nothing
Nó có phần rườm rà để chứng minh rằng không có Monad
trường hợp hợp pháp . Lý do cho hành vi không đơn điệu là không có cách thực hiện tự nhiên bind
khi một hàm f :: a -> B b
có thể trả về Nothing
hoặc Just
cho các giá trị khác nhau của a
.
Có lẽ rõ ràng hơn để xem xét Maybe (a, a, a)
, đó cũng không phải là một đơn nguyên, và cố gắng thực hiện join
cho điều đó. Người ta sẽ thấy rằng không có cách thực hiện hợp lý trực giác join
.
join :: Maybe (Maybe (a, a, a), Maybe (a, a, a), Maybe (a, a, a)) -> Maybe (a, a, a)
join Nothing = Nothing
join Just (Nothing, Just (x1,x2,x3), Just (y1,y2,y3)) = ???
join Just (Just (x1,x2,x3), Nothing, Just (y1,y2,y3)) = ???
-- etc.
Trong các trường hợp được chỉ ra bởi ???
, có vẻ như rõ ràng rằng chúng ta không thể sản xuất Just (z1, z2, z3)
theo bất kỳ cách hợp lý và đối xứng nào trong số sáu giá trị khác nhau của loại a
. Chúng tôi chắc chắn có thể chọn một số tập hợp con tùy ý của sáu giá trị này, - ví dụ, luôn luôn lấy số không đầu tiên Maybe
- nhưng điều này sẽ không thỏa mãn luật của đơn nguyên. Trở về Nothing
cũng sẽ không thỏa mãn luật pháp.
- Một cấu trúc dữ liệu giống như cây không phải là một đơn nguyên mặc dù nó có tính kết hợp cho
bind
- nhưng không thành công các luật định danh.
Đơn nguyên giống như cây thông thường (hoặc "một cây có cành hình functor") được định nghĩa là
data Tr f a = Leaf a | Branch (f (Tr f a))
Đây là một đơn nguyên miễn phí trên functor f
. Hình dạng của dữ liệu là một cây trong đó mỗi điểm nhánh là một "functor-Ful" của cây con. Cây nhị phân tiêu chuẩn sẽ thu được với type f a = (a, a)
.
Nếu chúng ta sửa đổi cấu trúc dữ liệu này bằng cách tạo ra những chiếc lá có hình dạng của functor f
, chúng ta sẽ có được cái mà tôi gọi là "semimonad" - nó có bind
thể thỏa mãn tính tự nhiên và luật kết hợp, nhưng pure
phương pháp của nó không thành một trong các luật định danh. "Semimonads là nhóm bán kết trong danh mục endofunctor, vấn đề là gì?" Đây là lớp loại Bind
.
Để đơn giản, tôi định nghĩa join
phương thức thay vì bind
:
data Trs f a = Leaf (f a) | Branch (f (Trs f a))
join :: Trs f (Trs f a) -> Trs f a
join (Leaf ftrs) = Branch ftrs
join (Branch ftrstrs) = Branch (fmap @f join ftrstrs)
Việc ghép cành là chuẩn, nhưng việc ghép lá là không chuẩn và tạo ra a Branch
. Đây không phải là một vấn đề đối với luật kết hợp nhưng vi phạm một trong các luật định danh.
Khi nào các loại đa thức có trường hợp đơn nguyên?
Không ai trong số các chức năng Maybe (a, a)
và Maybe (a, a, a)
có thể được đưa ra một Monad
ví dụ hợp pháp , mặc dù họ rõ ràng Applicative
.
Các functor này không có thủ thuật - không Void
hoặc bottom
bất cứ nơi nào, không có sự lười biếng / nghiêm ngặt khó khăn, không có cấu trúc vô hạn và không có ràng buộc lớp loại. Các Applicative
ví dụ là hoàn toàn chuẩn. Các chức năng return
và bind
có thể được thực hiện cho các functor này nhưng sẽ không thỏa mãn luật pháp của đơn nguyên. Nói cách khác, các functor này không phải là đơn nguyên vì một cấu trúc cụ thể bị thiếu (nhưng không dễ để hiểu chính xác những gì còn thiếu). Ví dụ, một thay đổi nhỏ trong functor có thể biến nó thành một đơn nguyên: data Maybe a = Nothing | Just a
là một đơn nguyên. Một functor tương tự khác data P12 a = Either a (a, a)
cũng là một đơn nguyên.
Xây dựng cho các đơn nguyên đa thức
Nói chung, đây là một số công trình sản xuất hợp pháp Monad
ra khỏi các loại đa thức. Trong tất cả các công trình này, M
là một đơn nguyên:
type M a = Either c (w, a)
nơi w
nào monoid
type M a = m (Either c (w, a))
nơi m
bất kỳ đơn nguyên và w
là bất kỳ monoid
type M a = (m1 a, m2 a)
nơi m1
và m2
bất kỳ monads
type M a = Either a (m a)
nơi m
bất kỳ đơn nguyên
Công trình thứ nhất là WriterT w (Either c)
, công trình thứ hai là WriterT w (EitherT c m)
. Cấu trúc thứ ba là một sản phẩm thành phần của các đơn vị: pure @M
được định nghĩa là sản phẩm thành phần của pure @m1
và pure @m2
, và join @M
được xác định bằng cách bỏ qua dữ liệu sản phẩm chéo (ví dụ: m1 (m1 a, m2 a)
được ánh xạ m1 (m1 a)
bằng cách bỏ qua phần thứ hai của bộ dữ liệu):
join :: (m1 (m1 a, m2 a), m2 (m1 a, m2 a)) -> (m1 a, m2 a)
join (m1x, m2x) = (join @m1 (fmap fst m1x), join @m2 (fmap snd m2x))
Công trình thứ tư được định nghĩa là
data M m a = Either a (m a)
instance Monad m => Monad M m where
pure x = Left x
join :: Either (M m a) (m (M m a)) -> M m a
join (Left mma) = mma
join (Right me) = Right $ join @m $ fmap @m squash me where
squash :: M m a -> m a
squash (Left x) = pure @m x
squash (Right ma) = ma
Tôi đã kiểm tra rằng tất cả bốn công trình xây dựng các đơn nguyên hợp pháp.
Tôi phỏng đoán rằng không có công trình nào khác cho các đơn nguyên đa thức. Ví dụ, functor Maybe (Either (a, a) (a, a, a, a))
không thu được thông qua bất kỳ công trình nào và do đó không phải là đơn nguyên. Tuy nhiên, Either (a, a) (a, a, a)
là monadic vì nó là đẳng cấu với sản phẩm của ba monads a
, a
và Maybe a
. Ngoài ra, Either (a,a) (a,a,a,a)
là đơn sắc vì nó là đẳng cấu với sản phẩm của a
và Either a (a, a, a)
.
Bốn công trình được trình bày ở trên sẽ cho phép chúng tôi có được bất kỳ số tiền nào của bất kỳ số lượng sản phẩm nào của bất kỳ số lượng nào a
, ví dụ Either (Either (a, a) (a, a, a, a)) (a, a, a, a, a))
, v.v. Tất cả các hàm tạo kiểu như vậy sẽ có (ít nhất một) Monad
thể hiện.
Tất nhiên vẫn còn phải xem, trường hợp sử dụng nào có thể tồn tại cho các đơn nguyên như vậy. Một vấn đề khác là các Monad
trường hợp xuất phát qua các công trình 1-4 nói chung không phải là duy nhất. Ví dụ, hàm tạo kiểu type F a = Either a (a, a)
có thể được cung cấp một Monad
thể hiện theo hai cách: bằng cách xây dựng 4 bằng cách sử dụng đơn nguyên (a, a)
và bằng cách xây dựng 3 bằng cách sử dụng kiểu đẳng cấu Either a (a, a) = (a, Maybe a)
. Một lần nữa, việc tìm kiếm các trường hợp sử dụng cho các triển khai này không rõ ràng ngay lập tức.
Một câu hỏi vẫn còn - được đưa ra một kiểu dữ liệu đa thức tùy ý, làm thế nào để nhận biết liệu nó có một Monad
thể hiện hay không. Tôi không biết làm thế nào để chứng minh rằng không có công trình nào khác cho các đơn nguyên đa thức. Tôi không nghĩ rằng có bất kỳ lý thuyết nào tồn tại cho đến nay để trả lời câu hỏi này.
* -> *
) mà không tồn tại không phù hợpfmap
?