Những ví dụ điển hình về Không phải là Functor / Functor / Applicative / Monad?


209

Trong khi giải thích cho ai đó loại lớp X là gì, tôi đấu tranh để tìm các ví dụ tốt về cấu trúc dữ liệu chính xác là X.

Vì vậy, tôi yêu cầu các ví dụ cho:

  • Một constructor kiểu không phải là Functor.
  • Một hàm tạo kiểu là Functor, nhưng không áp dụng.
  • Một constructor kiểu là một Applicative, nhưng không phải là Monad.
  • Một kiểu xây dựng là một Monad.

Tôi nghĩ rằng có rất nhiều ví dụ về Monad ở khắp mọi nơi, nhưng một ví dụ hay về Monad với một số liên quan đến các ví dụ trước có thể hoàn thành bức tranh.

Tôi tìm kiếm các ví dụ tương tự nhau, chỉ khác nhau về các khía cạnh quan trọng để thuộc về loại loại cụ thể.

Nếu người ta có thể lén lấy một ví dụ về Mũi tên ở đâu đó trong hệ thống phân cấp này (có phải là giữa Applicative và Monad không?), Điều đó cũng sẽ rất tuyệt!


4
Có thể tạo một hàm tạo kiểu ( * -> *) mà không tồn tại không phù hợp fmap?
Owen

1
Owen, tôi nghĩ a -> Stringkhông phải là một functor.
Rotsor

3
@Rotsor @Owen a -> Stringlà một functor toán học, nhưng không phải là Haskell Functor, rõ ràng.
J. Abrahamson

@J. Abrahamson, theo nghĩa nào thì nó là một functor toán học? Bạn đang nói về thể loại với mũi tên đảo ngược?
Rotsor

3
Đối với những người không biết, một contravariant functor có fmap loại(a -> b) -> f b -> f a
AJFarmar

Câu trả lời:


100

Một hàm tạo kiểu không phải là Functor:

newtype T a = T (a -> Int)

Bạn có thể tạo ra một functor chống chỉ định từ nó, nhưng không phải là một functor (covariant). Hãy thử viết fmapvà bạn sẽ thất bại. Lưu ý rằng phiên bản functor chống chỉ định được đảo ngược:

fmap      :: Functor f       => (a -> b) -> f a -> f b
contramap :: Contravariant f => (a -> b) -> f b -> f a

Một constructor kiểu là một functor, nhưng không áp dụng:

Tôi không có một ví dụ tốt. Có Const, nhưng lý tưởng là tôi muốn một phi-cụ thể cụ thể và tôi không thể nghĩ ra cái nào. Tất cả các loại về cơ bản là số, liệt kê, sản phẩm, tổng hoặc hàm khi bạn truy cập vào nó. Bạn có thể thấy bên dưới pigworker và tôi không đồng ý về việc có phải Data.Voidlà một Monoid;

instance Monoid Data.Void where
    mempty = undefined
    mappend _ _ = undefined
    mconcat _ = undefined

_|_là một giá trị pháp lý trong Haskell và trên thực tế là giá trị pháp lý duy nhất của Data.Void, điều này đáp ứng các quy tắc Monoid. Tôi không chắc chắn những gì unsafeCoercephải làm với nó, bởi vì chương trình của bạn không còn được đảm bảo không vi phạm ngữ nghĩa Haskell ngay khi bạn sử dụng bất kỳ unsafechức năng nào .

Xem Haskell Wiki để biết một bài viết ở dưới cùng ( liên kết ) hoặc các chức năng ( liên kết ) không an toàn .

Tôi tự hỏi liệu có thể tạo ra một hàm tạo kiểu như vậy bằng cách sử dụng một hệ thống loại phong phú hơn, chẳng hạn như Agda hoặc Haskell với các phần mở rộng khác nhau.

Một hàm tạo kiểu là Ứng dụng, nhưng không phải là Monad:

newtype T a = T {multidimensional array of a}

Bạn có thể tạo một Ứng dụng từ nó, với một cái gì đó như:

mkarray [(+10), (+100), id] <*> mkarray [1, 2]
  == mkarray [[11, 101, 1], [12, 102, 2]]

Nhưng nếu bạn biến nó thành một đơn nguyên, bạn có thể có một sự không phù hợp về kích thước. Tôi nghi ngờ rằng những ví dụ như thế này rất hiếm trong thực tế.

Một hàm tạo kiểu là Monad:

[]

Về mũi tên:

Hỏi mũi tên nằm ở đâu trong hệ thống phân cấp này giống như hỏi "hình dạng" màu đỏ là gì. Lưu ý sự không phù hợp:

Functor :: * -> *
Applicative :: * -> *
Monad :: * -> *

nhưng,

Arrow :: * -> * -> *

3
Danh sách tốt! Tôi sẽ đề nghị sử dụng một cái gì đó đơn giản hơn như Either amột ví dụ cho trường hợp cuối cùng, vì nó dễ hiểu hơn.
fuz

6
Nếu bạn vẫn đang tìm kiếm một hàm tạo kiểu ứng dụng nhưng không phải là Monad, một ví dụ rất phổ biến sẽ là ZipList.
John L

23
_|_Sinh sống ở mọi loại trong *, nhưng điểm quan trọng Voidlà bạn phải cúi xuống để xây dựng một hoặc bạn đã phá hủy giá trị của nó. Đây là lý do tại sao nó không phải là một thể hiện của Enum, monoid, vv Nếu bạn đã có một, tôi vui mừng báo cho bạn ngâm chúng lại với nhau (tạo cho bạn một Semigroup) nhưng mempty, nhưng tôi cho không có công cụ để xây dựng một cách rõ ràng giá trị của loại Voidtrong void. Bạn phải nạp súng và chĩa súng vào chân và tự bóp cò.
Edward KMett

2
Về mặt giáo dục, tôi nghĩ rằng quan điểm của bạn về Cofunctor là sai. Dual của một functor là một functor, bởi vì bạn lật cả đầu vào đầu ra và kết thúc với cùng một thứ. Khái niệm bạn đang tìm kiếm có lẽ là "contravariant functor", hơi khác một chút.
Ben Millwood

1
@AlexVong: "Không dùng nữa" -> mọi người chỉ đang sử dụng một gói khác. Nói về "contravariant functor" không phải "dual of functor", xin lỗi vì sự nhầm lẫn. Trong một số bối cảnh tôi đã thấy "cofunctor" được sử dụng để đề cập đến "functor chống chỉ định" bởi vì functor là tự đối ngẫu, nhưng nó dường như chỉ gây nhầm lẫn cho mọi người.
Dietrich Epp

87

Phong cách của tôi có thể bị chật chội bởi điện thoại của tôi, nhưng ở đây đi.

newtype Not x = Kill {kill :: x -> Void}

không thể là một Functor. Nếu có, chúng ta sẽ có

kill (fmap (const ()) (Kill id)) () :: Void

và Mặt trăng sẽ được làm bằng phô mai xanh.

Trong khi đó

newtype Dead x = Oops {oops :: Void}

là một chức năng

instance Functor Dead where
  fmap f (Oops corpse) = Oops corpse

nhưng không thể áp dụng, hoặc chúng ta sẽ có

oops (pure ()) :: Void

và Green sẽ được làm từ phô mai Mặt trăng (điều này thực sự có thể xảy ra, nhưng chỉ muộn hơn vào buổi tối).

(Ghi chú thêm : Void, như Data.Voidlà một kiểu dữ liệu trống. Nếu bạn cố gắng sử dụng undefinedđể chứng minh đó là Monoid, tôi sẽ sử dụng unsafeCoerceđể chứng minh rằng nó không phải.)

Vui vẻ,

newtype Boo x = Boo {boo :: Bool}

được áp dụng theo nhiều cách, ví dụ, như Dijkstra sẽ có nó,

instance Applicative Boo where
  pure _ = Boo True
  Boo b1 <*> Boo b2 = Boo (b1 == b2)

nhưng nó không thể là một Monad. Để xem tại sao không, quan sát rằng sự trở lại phải liên tục Boo Truehoặc Boo False, và do đó

join . return == id

không thể giữ

Ồ vâng, tôi gần như quên mất

newtype Thud x = The {only :: ()}

là một đơn nguyên. Cuộn của riêng bạn.

Máy bay để bắt ...


8
Vô vi là trống rỗng! Về mặt đạo đức, dù sao đi nữa.
thợ lợn

9
Void là một loại có 0 constructor, tôi giả sử. Nó không phải là một monoid bởi vì không có mempty.
Rotsor

6
chưa xác định? Thật thô lỗ! Đáng buồn thay, unsafeCoerce (unsafeCoerce () <*> không xác định) không phải là (), vì vậy trong cuộc sống thực, có những quan sát vi phạm luật pháp.
thợ lợn

5
Trong ngữ nghĩa thông thường, chấp nhận chính xác một loại không xác định, bạn hoàn toàn đúng. Có những ngữ nghĩa khác, tất nhiên. Void không giới hạn một submonoid trong tổng số mảnh. Nó cũng không phải là một monoid trong một ngữ nghĩa phân biệt các chế độ thất bại. Khi tôi có một khoảnh khắc dễ dàng hơn so với chỉnh sửa dựa trên điện thoại, tôi sẽ làm rõ rằng ví dụ của tôi chỉ hoạt động trong một ngữ nghĩa mà không có chính xác một loại không xác định.
thợ lợn

22
Phần lớn quảng cáo về_|_
Landei

71

Tôi tin rằng các câu trả lời khác đã bỏ lỡ một số ví dụ đơn giản và phổ biến:

Một constructor kiểu là Functor nhưng không phải là Applicative. Một ví dụ đơn giản là một cặp:

instance Functor ((,) r) where
    fmap f (x,y) = (x, f y)

Nhưng không có cách nào để xác định thể hiện của nó Applicativemà không áp đặt các hạn chế bổ sung r. Cụ thể, không có cách nào để xác định pure :: a -> (r, a)cho một tùy ý r.

Một constructor kiểu là một Applicative, nhưng không phải là Monad. Một ví dụ nổi tiếng là ZipList . (Đó là một newtypedanh sách kết thúc danh sách và cung cấp Applicativeví dụ khác cho chúng.)

fmapđược định nghĩa theo cách thông thường. Nhưng pure<*>được định nghĩa là

pure x                    = ZipList (repeat x)
ZipList fs <*> ZipList xs = ZipList (zipWith id fs xs)

để puretạo ra một danh sách vô hạn bằng cách lặp lại giá trị nhất định, và <*>kéo khóa một danh sách các hàm với một danh sách các giá trị - áp dụng i chức năng -thứ để tôi -thứ phần tử. (Tiêu chuẩn <*>về []sản xuất tất cả các kết hợp có thể áp dụng i chức năng -thứ để j yếu tố -thứ.) Nhưng không có cách nào hợp lý như thế nào để xác định một đơn nguyên (xem bài này ).


Làm thế nào mũi tên phù hợp với hệ thống phân cấp functor / ứng dụng / đơn nguyên? Xem Thành ngữ là lãng quên, mũi tên là tỉ mỉ, các đơn vị được lăng nhăng bởi Sam Lindley, Philip Wadler, Jeremy Yallop. MSFP 2008 (Họ gọi các thành ngữ functor ứng dụng .) Tóm tắt:

Chúng tôi xem xét lại mối liên hệ giữa ba khái niệm tính toán: đơn nguyên của Moggi, mũi tên của Hughes và thành ngữ của McBride và Paterson (còn được gọi là functor ứng dụng). Chúng tôi chỉ ra rằng các thành ngữ tương đương với các mũi tên thỏa mãn kiểu đồng phân loại A ~> B = 1 ~> (A -> B) và các đơn vị tương đương với các mũi tên thỏa mãn kiểu đồng phân loại A ~> B = A -> (1 ~ > B). Hơn nữa, thành ngữ nhúng vào mũi tên và mũi tên nhúng vào đơn nguyên.


1
Vì vậy, ((,) r)một functor không phải là một ứng dụng; nhưng điều này chỉ bởi vì bạn thường không thể xác định puretất cả rcùng một lúc. Do đó, đó là một sự châm biếm về sự đồng nhất ngôn ngữ, về việc cố gắng xác định một bộ sưu tập các hàm xử lý ứng dụng (vô hạn) với một định nghĩa về pure<*>; theo nghĩa này, dường như không có bất cứ điều gì sâu sắc về mặt toán học về ví dụ phản biện này vì, đối với bất kỳ cụ thể nào r, ((,) r) đều có thể được thực hiện một chức năng ứng dụng. Câu hỏi: Bạn có thể nghĩ về một functor BÊ TÔNG mà không phải là một ứng dụng?
George

1
Xem stackoverflow.com/questions/44125484/ trên như bài đăng với câu hỏi này.
George

20

Một ví dụ điển hình cho hàm tạo kiểu không phải là hàm functor là Set: Bạn không thể thực hiện fmap :: (a -> b) -> f a -> f b, vì không có ràng buộc bổ sung Ord bnào bạn không thể xây dựng f b.


16
Nó thực sự là một ví dụ tốt vì về mặt toán học, chúng tôi thực sự muốn biến điều này thành một functor.
Alexandre C.

21
@AlexandreC. Tôi không đồng ý về điều đó, nó không phải là một ví dụ tốt. Về mặt toán học, cấu trúc dữ liệu như vậy tạo thành một functor. Thực tế là chúng tôi không thể thực hiện fmapchỉ là một vấn đề ngôn ngữ / thực hiện. Ngoài ra, có thể kết Sethợp với đơn nguyên tiếp tục, điều này tạo ra một đơn nguyên từ tất cả các thuộc tính mà chúng ta mong đợi, xem câu hỏi này (mặc dù tôi không chắc liệu nó có thể được thực hiện hiệu quả không).
Petr Pudlák

@PetrPudlak, đây là vấn đề ngôn ngữ như thế nào? Bình đẳng bcó thể là không thể giải quyết được, trong trường hợp đó bạn không thể xác định fmap!
Turion

@Turion Có thể quyết định và dứt khoát là hai điều khác nhau. Ví dụ, có thể xác định chính xác sự bình đẳng trên các thuật ngữ (chương trình) lambda, mặc dù không thể quyết định bằng thuật toán. Trong mọi trường hợp, đây không phải là trường hợp của ví dụ này. Vấn đề ở đây là chúng ta không thể định nghĩa một Functorthể hiện với Ordràng buộc, nhưng có thể với một định nghĩa khác về Functorhỗ trợ ngôn ngữ tốt hơn. Trên thực tế với ConstraintKinds có thể định nghĩa một lớp loại có thể được tham số hóa như thế này.
Petr Pudlák

Ngay cả khi chúng ta có thể vượt qua các ordràng buộc thì thực tế là Setkhông thể chứa các mục trùng lặp có nghĩa là fmapcó thể thay thế bối cảnh. Điều này vi phạm luật kết hợp.
John F. Miller

11

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:

  1. 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.
  2. 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ể

  • Một hàm tạo kiểu không thể có một cá thể functor vì kiểu này không thể được thực thi:

    data F z a = F (a -> z)

Đâ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 fmapcó 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ù Fkhông thể là functor vì nó sử dụng aở vị trí chống chỉ định. Vì vậy, việc triển khai fmaphiể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 idid, vì let (Q(f,_)) = fmap id (Q(read,"123")) in f "456"123, nhưng let (Q(f,_)) = id (Q(read,"123")) in f "456"456.

Trong thực tế, Fchỉ 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 purekhô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 wphả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 Plà một functor vì nó achỉ 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 Applicativenhưng không phải làMonad vì chữ ký loại bindkhông thể được thực hiện.

Tôi không biết bất kỳ ví dụ như vậy!

  • Một functor Applicativenhư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 bindcó 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ó Monadtrườ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 bindkhi một hàm f :: a -> B bcó thể trả về Nothinghoặc Justcho 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 joincho đ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ề Nothingcũ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ó bindthể thỏa mãn tính tự nhiên và luật kết hợp, nhưng purephươ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 joinphươ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)Maybe (a, a, a)có thể được đưa ra một Monadví 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 Voidhoặc bottombấ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 Applicativeví dụ là hoàn toàn chuẩn. Các chức năng returnbindcó 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 alà 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 Monadra khỏi các loại đa thức. Trong tất cả các công trình này, Mlà một đơn nguyên:

  1. type M a = Either c (w, a)nơi wnào monoid
  2. type M a = m (Either c (w, a))nơi mbất kỳ đơn nguyên và wlà bất kỳ monoid
  3. type M a = (m1 a, m2 a)nơi m1m2bất kỳ monads
  4. type M a = Either a (m a)nơi mbấ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 @m1pure @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, aMaybe 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 aEither 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) Monadthể 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 Monadtrườ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 Monadthể 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 Monadthể 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.


Tôi nghĩ B một đơn nguyên. Bạn có thể đưa ra một ví dụ cho liên kết này Pair x y >>= f = case (f x, f y) of (Pair x' _,Pair _ y') -> Pair x' y' ; _ -> Empty?
Franky

@Franky Associativity không thành công với định nghĩa này khi bạn chọn fnhư vậy f xEmptynhưng f ylà một Pair, và trên bước tiếp theo cả hai đều Pair. Tôi đã kiểm tra bằng tay rằng các luật không giữ cho việc thực hiện này hoặc cho bất kỳ thực hiện nào khác. Nhưng đó là một chút công việc để làm điều đó. Tôi ước có một cách dễ dàng hơn để tìm ra điều này!
winitzki

1
@Turion Đối số đó không áp dụng MaybeMaybekhông chứa các giá trị khác nhau ađể lo lắng.
Daniel Wagner

1
@Turion Tôi đã chứng minh điều này bằng một vài trang tính toán; lập luận về "cách tự nhiên" chỉ là một lời giải thích heuristic. Một Monadthể hiện bao gồm các hàm returnbindthỏa mãn các luật. Có hai triển khai returnvà 25 triển khai bindphù hợp với các loại yêu cầu. Bạn có thể chỉ ra bằng cách tính toán trực tiếp rằng không có sự thực thi nào thỏa mãn luật pháp. Để cắt giảm số lượng công việc cần thiết, tôi đã sử dụng jointhay vì bindvà sử dụng luật định danh trước tiên. Nhưng đó là một chút công việc.
winitzki

1
@duplode Không, tôi không nghĩ Traversablelà cần thiết. m (Either a (m a))được chuyển đổi bằng cách sử dụng pure @mthành m (Either (m a) (m a)). Sau đó, tầm thường Either (m a) (m a) -> m a, và chúng ta có thể sử dụng join @m. Đó là việc thực hiện mà tôi đã kiểm tra luật pháp.
winitzki
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.