ตัวอย่างที่ดีของ Not a Functor / Functor / Applicative / Monad?


210

ในขณะที่อธิบายให้คนฟังว่า class X ชนิดใดที่ฉันพยายามหาตัวอย่างที่ดีของโครงสร้างข้อมูลซึ่งเป็น X อย่างแน่นอน

ดังนั้นฉันขอตัวอย่างสำหรับ:

  • คอนสตรัคเตอร์ชนิดที่ไม่ใช่ Functor
  • ตัวสร้างประเภทซึ่งเป็น Functor แต่ไม่สามารถใช้
  • คอนสตรัคเตอร์ชนิดซึ่งเป็นการใช้งาน แต่ไม่ใช่ Monad
  • ตัวสร้างประเภทซึ่งเป็น Monad

ฉันคิดว่ามีตัวอย่างมากมายของ Monad ทุกที่ แต่ตัวอย่างที่ดีของ Monad ที่เกี่ยวข้องกับตัวอย่างก่อนหน้านี้อาจทำให้ภาพสมบูรณ์

ฉันมองหาตัวอย่างที่จะคล้ายกันแตกต่างกันเฉพาะในแง่มุมที่สำคัญสำหรับการเป็นของประเภทประเภทเฉพาะ

หากมีใครสามารถจัดการตัวอย่างของ Arrow ที่อยู่ในลำดับชั้นนี้ได้ (มันอยู่ระหว่าง Applicative และ Monad หรือไม่) นั่นก็ยอดเยี่ยมเช่นกัน!


4
เป็นไปได้หรือไม่ที่จะสร้างตัวสร้างแบบ ( * -> *) ซึ่งไม่มีความเหมาะสมfmap?
โอเว่น

1
โอเว่นฉันคิดว่าa -> Stringไม่ใช่นักแสดง
Rotsor

3
@Rotsor @Owen a -> Stringเป็น functor ทางคณิตศาสตร์ แต่ไม่ใช่ Haskell Functorเพื่อให้ชัดเจน
J. Abrahamson

@J Abrahamson มันเป็นฟังก์ชั่นทางคณิตศาสตร์ในความหมายอะไร? คุณกำลังพูดถึงหมวดหมู่ที่มีลูกศรกลับด้านหรือไม่
Rotsor

3
สำหรับคนที่ไม่ทราบว่าเป็นcontravariant functor มี fmap ประเภท(a -> b) -> f b -> f a
AJFarmar

คำตอบ:


100

ตัวสร้างประเภทที่ไม่ใช่ Functor:

newtype T a = T (a -> Int)

คุณสามารถสร้าง functor ที่แตกต่างออกไปได้ แต่ไม่ใช่ functor (covariant) ลองเขียนfmapและคุณจะล้มเหลว โปรดทราบว่าเวอร์ชัน functor ที่ตรงกันข้ามนั้นกลับด้าน:

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

คอนสตรัคเตอร์ประเภทซึ่งเป็น functor แต่ไม่สามารถใช้งานได้:

ฉันไม่มีตัวอย่างที่ดี มีConstแต่นึกคิดฉันต้องการคอนกรีตไม่ใช่ -Moid และฉันไม่สามารถคิดใด ๆ ทุกประเภทนั้นเป็นตัวเลขการแจกแจงผลิตภัณฑ์ผลรวมหรือฟังก์ชั่นเมื่อคุณลงไป คุณสามารถดูด้านล่าง pigworker และฉันไม่เห็นด้วยเกี่ยวกับว่าData.Voidเป็นMonoid;

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

เนื่องจาก_|_เป็นมูลค่าทางกฎหมายใน Haskell และในความเป็นจริงมูลค่าทางกฎหมายเพียงอย่างเดียวData.Voidจึงเป็นไปตามกฎ Monoid ฉันไม่แน่ใจว่าunsafeCoerceจะทำอย่างไรกับมันเพราะโปรแกรมของคุณไม่รับประกันว่าจะไม่ละเมิดความหมายของ Haskell ทันทีที่คุณใช้unsafeฟังก์ชั่นใด ๆ

ดู Haskell Wiki สำหรับบทความด้านล่าง ( ลิงค์ ) หรือฟังก์ชั่นที่ไม่ปลอดภัย ( ลิงค์ )

ฉันสงสัยว่าเป็นไปได้หรือไม่ที่จะสร้างตัวสร้างประเภทดังกล่าวโดยใช้ระบบประเภทที่สมบูรณ์ยิ่งขึ้นเช่น Agda หรือ Haskell ที่มีนามสกุลต่าง ๆ

คอนสตรัคเตอร์ชนิดซึ่งเป็นการใช้งาน แต่ไม่ใช่แบบ Monad:

newtype T a = T {multidimensional array of a}

คุณสามารถใช้งาน Applicative ได้โดยมีลักษณะดังนี้:

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

แต่ถ้าคุณทำให้มันเป็น monad คุณอาจได้มิติที่ไม่ตรงกัน ฉันสงสัยว่าตัวอย่างเช่นนี้หายากในทางปฏิบัติ

ตัวสร้างประเภทซึ่งเป็น Monad:

[]

เกี่ยวกับลูกศร:

การถามว่าลูกศรอยู่ที่ใดในลำดับชั้นนี้เหมือนกับถามว่ารูปร่าง "สีแดง" คืออะไร หมายเหตุประเภทไม่ตรงกัน:

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

แต่,

Arrow :: * -> * -> *

3
รายการที่ดี! ฉันขอแนะนำให้ใช้สิ่งที่เรียบง่ายเหมือนEither aเป็นตัวอย่างสำหรับกรณีสุดท้ายเพราะเข้าใจง่ายกว่า
fuz

6
หากคุณยังคงมองหาคอนสตรัคชนิดที่ปรับใช้ แต่ไม่ Monad ZipListเป็นตัวอย่างที่พบบ่อยมากจะเป็น
John L

23
_|_พำนักอยู่ในทุกประเภทใน * แต่ประเด็นVoidคือคุณควรโค้งงอไปด้านหลังเพื่อสร้างหนึ่งหรือคุณทำลายคุณค่าของมัน นี่คือสาเหตุที่ไม่ใช่ตัวอย่างของ Enum, Monoid ฯลฯ หากคุณมีอยู่แล้วฉันยินดีที่จะให้คุณรวมเข้าด้วยกัน (ให้คุณSemigroup) memptyแต่ฉันไม่ได้ให้เครื่องมือในการสร้างมูลค่าประเภทอย่างชัดเจนVoidในvoid. คุณต้องโหลดปืนและชี้ไปที่เท้าของคุณและดึงไกปืนด้วยตัวคุณเอง
Edward KMETT

2
ฉันคิดว่า Cofunctor ของคุณคิดผิด คู่ของ functor เป็น functor เพราะคุณพลิกทั้งอินพุตและเอาต์พุตและจบลงด้วยสิ่งเดียวกัน ความคิดที่คุณกำลังมองหาน่าจะเป็น "functor contravariant" ซึ่งแตกต่างกันเล็กน้อย
Ben Millwood

1
@AlexVong: "เลิกใช้แล้ว" -> ผู้คนกำลังใช้แพ็คเกจอื่นอยู่ พูดคุยเกี่ยวกับ "นักแสดงตลกที่แตกต่าง" ไม่ใช่ "คู่ของนักแสดง" ขออภัยในความสับสน ในบริบทบางอย่างที่ฉันเคยเห็น "cofunctor" เคยอ้างถึง "contravariant functors" เพราะ functors เป็นแบบดูอัลเอง แต่ดูเหมือนว่าจะทำให้ผู้คนสับสน
Dietrich Epp

87

สไตล์ของฉันอาจแออัดโดยโทรศัพท์ของฉัน แต่ที่นี่จะไป

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

ไม่สามารถเป็น Functor ถ้าเป็นเช่นนั้นเราจะมี

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

และดวงจันทร์ก็ทำจากชีสสีเขียว

ในขณะเดียวกัน

newtype Dead x = Oops {oops :: Void}

เป็นนักแสดง

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

แต่ไม่สามารถสมัครได้หรือเรามี

oops (pure ()) :: Void

และสีเขียวจะทำจาก Moon moon (ซึ่งสามารถเกิดขึ้นได้จริง แต่ในตอนเย็นเท่านั้น)

(หมายเหตุเพิ่มเติม: Voidในขณะที่Data.Voidเป็นประเภทข้อมูลที่ว่างเปล่าหากคุณพยายามที่จะใช้undefinedเพื่อพิสูจน์ว่าเป็น Monoid ฉันจะใช้unsafeCoerceเพื่อพิสูจน์ว่ามันไม่ใช่)

เพลิน,

newtype Boo x = Boo {boo :: Bool}

มีหลายวิธีเช่นในแบบที่ Dijkstra มี

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

แต่มันไม่สามารถเป็น Monad ได้ หากต้องการดูว่าทำไมไม่ให้สังเกตว่าการกลับมานั้นต้องอยู่ตลอดเวลาBoo TrueหรือBoo Falseด้วยเหตุนี้

join . return == id

ไม่สามารถถือได้

โอ้ใช่ฉันเกือบจะลืม

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

เป็น Monad ม้วนของคุณเอง

เครื่องบินที่จะจับ ...


8
ว่างเปล่าว่างเปล่า! ทางศีลธรรม แต่อย่างใด
หมู

9
Void เป็นประเภทที่มีตัวสร้าง 0 ฉันถือว่า memptyมันไม่ได้เป็นหนังสือเพราะไม่มี
Rotsor

6
ไม่ได้กำหนด? หยาบคาย! น่าเศร้าที่ unsafeCoerce (unsafeCoerce () <*> ไม่ได้กำหนด) ไม่ได้เป็น () ดังนั้นในชีวิตจริงจึงมีข้อสังเกตที่ละเมิดกฎหมาย
หมู

5
ในความหมายปกติที่ทนไม่ได้ชนิดเดียวคุณค่อนข้างถูกต้อง แน่นอนว่ามีความหมายอื่น ๆ Void ไม่ได้ จำกัด อยู่ที่ submonoid ในส่วนทั้งหมด และมันก็ไม่ได้เป็นเพียงความหมายเดียวในความหมายซึ่งจำแนกโหมดของความล้มเหลว เมื่อฉันมีเวลากับการแก้ไขที่ง่ายกว่าการใช้โทรศัพท์ฉันจะชี้แจงว่าตัวอย่างของฉันใช้งานได้ในความหมายที่ไม่ได้กำหนดอย่างใดอย่างหนึ่งเท่านั้น
pigworker

22
กังวลใจมาก_|_
Landei

71

ฉันเชื่อว่าคำตอบอื่น ๆ พลาดตัวอย่างง่ายๆและทั่วไป:

คอนสตรัคเตอร์ประเภทซึ่งเป็น Functor แต่ไม่ได้ใช้งาน ตัวอย่างง่ายๆคือคู่:

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

แต่มีวิธีวิธีการกำหนดของมันไม่Applicativeตัวอย่างเช่นการจัดเก็บภาษีโดยไม่ต้องมีข้อ จำกัด rเพิ่มเติมเกี่ยวกับ โดยเฉพาะอย่างยิ่งไม่มีวิธีกำหนดpure :: a -> (r, a)โดยพลการใดr

คอนสตรัคเตอร์ชนิดซึ่งเป็นการใช้งาน แต่ไม่ใช่ Monad ตัวอย่างที่รู้จักกันดีคือZipList (เป็นnewtypeรายการที่ตัดคำและให้Applicativeอินสแตนซ์ต่างกันสำหรับพวกเขา)

fmapถูกกำหนดในวิธีปกติ แต่pureและ<*>ถูกกำหนดให้เป็น

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

เพื่อpureสร้างรายการที่ไม่มีที่สิ้นสุดโดยการทำซ้ำค่าที่กำหนดและ<*>รหัสไปรษณีย์รายชื่อของฟังก์ชั่นที่มีรายชื่อของค่า - ใช้ฉันฟังก์ชั่นการ -th ฉันองค์ประกอบ -th (มาตรฐาน<*>ในการ[]สร้างชุดที่เป็นไปได้ทั้งหมดของการใช้ฟังก์ชั่นi- th กับองค์ประกอบj -th.) แต่ไม่มีวิธีที่เหมาะสมวิธีการกำหนด Monad (ดูโพสต์นี้ )


ลูกศรพอดีกับลำดับชั้นของ functor / applicative / monad อย่างไร ดูสำนวนที่ลืมเลือนลูกธนูมีความพิถีพิถันพระสงฆ์ต่างให้ความสำคัญโดย Sam Lindley, Philip Wadler, Jeremy Yallop MSFP 2008. (พวกเขาเรียกใช้สำนวนฟังก์ชั่นการใช้งาน) บทคัดย่อ:

เราทบทวนการเชื่อมโยงระหว่างสามแนวคิดของการคำนวณ: Monads ของ Moggi, ลูกศรของ Hughes และ McBride และสำนวนของ Paterson (หรือเรียกอีกอย่างว่าฟังก์ชันการใช้งาน) เราแสดงให้เห็นว่าสำนวนนั้นเทียบเท่ากับลูกศรที่ตอบสนองประเภท isomorphism A ~> B = 1 ~> (A -> B) และ monads นั้นเทียบเท่ากับลูกศรที่สนองประเภท isomorphism A ~> B = A -> (1 ~ > B) นอกจากนี้สำนวนที่ฝังอยู่ในลูกศรและลูกศรก็ฝังไว้ในพระ


1
ดังนั้น((,) r)นักแสดงที่ไม่ได้สมัครก็คือ แต่นี่เป็นเพียงเพราะคุณไม่สามารถกำหนดได้pureทั้งหมดrในครั้งเดียว ดังนั้นจึงเป็นเรื่องแปลกของความกระชับของภาษาพยายามที่จะกำหนดคอลเลกชัน (ไม่ จำกัด ) ของฟังก์ชั่นการใช้งานที่มีความหมายหนึ่งpureและ<*>; ในความรู้สึกนี้มีไม่ดูเหมือนจะเป็นสิ่งที่ทางคณิตศาสตร์ลึกเกี่ยวกับเรื่องนี้ที่เคาน์เตอร์ตัวอย่างตั้งแต่คอนกรีตใด ๆr, ((,) r) สามารถจะทำ functor applicative คำถาม: คุณนึกถึงนักปั้นคอนกรีตที่ล้มเหลวในการสมัครได้หรือไม่?
จอร์จ

1
ดูstackoverflow.com/questions/44125484/…เป็นโพสต์ด้วยคำถามนี้
จอร์จ

20

ตัวอย่างที่ดีสำหรับตัวสร้างประเภทที่ไม่ใช่ functor คือSet: คุณไม่สามารถใช้งานได้fmap :: (a -> b) -> f a -> f bเนื่องจากไม่มีข้อ จำกัด เพิ่มเติมOrd bคุณไม่สามารถสร้างf bได้


16
มันเป็นจริงเป็นตัวอย่างที่ดีตั้งแต่ทางคณิตศาสตร์เราจะจริงๆเช่นนี้เพื่อให้ functor
Alexandre C.

21
@AlexandreC ฉันไม่เห็นด้วยกับสิ่งนั้นมันไม่ใช่ตัวอย่างที่ดี ในทางคณิตศาสตร์โครงสร้างข้อมูลดังกล่าวทำให้เกิด functor ความจริงที่ว่าเราไม่สามารถใช้งานได้fmapเป็นเพียงปัญหาด้านภาษา / การใช้งาน นอกจากนี้ยังเป็นไปได้ที่จะห่อSetลงใน monad ที่ต่อเนื่องกันซึ่งทำให้ monad ออกมาจากคุณสมบัติทั้งหมดที่เราคาดหวังดูคำถามนี้ (แม้ว่าฉันไม่แน่ใจว่ามันสามารถทำได้อย่างมีประสิทธิภาพ)
Petr Pudlák

@PetrPudlak ปัญหานี้เกิดขึ้นได้อย่างไร ความเท่าเทียมกันของbอาจจะไม่สามารถตัดสินใจได้ในกรณีนี้คุณไม่สามารถกำหนดได้fmap!
Turion

@Turion การสามารถตัดสินใจได้และกำหนดได้นั้นเป็นสองสิ่งที่แตกต่างกัน ตัวอย่างเช่นเป็นไปได้ที่จะกำหนดความเท่าเทียมกันอย่างถูกต้องเกี่ยวกับคำแลมบ์ดา (โปรแกรม) แม้ว่ามันจะเป็นไปไม่ได้ที่จะตัดสินใจโดยอัลกอริทึม ไม่ว่าในกรณีใด ๆ นี่ไม่ใช่กรณีของตัวอย่างนี้ นี่คือปัญหาคือเราไม่สามารถกำหนดFunctorอินสแตนซ์ที่มีOrdข้อ จำกัด แต่อาจเป็นไปได้ด้วยคำจำกัดความที่แตกต่างกันFunctorหรือการสนับสนุนทางภาษาที่ดีกว่า ที่จริงแล้วด้วย ConstraintKinds มันเป็นไปได้ที่จะกำหนด class type ที่สามารถ parametrized ได้เช่นนี้
Petr Pudlák

แม้ว่าเราจะสามารถเอาชนะordข้อ จำกัด ได้ความจริงที่ว่าSetไม่สามารถมีรายการที่ซ้ำกันหมายความว่าfmapสามารถให้บริบท สิ่งนี้ละเมิดกฎหมายการเชื่อมโยง
John F. Miller

11

ฉันต้องการเสนอวิธีการที่เป็นระบบมากขึ้นในการตอบคำถามนี้และเพื่อแสดงตัวอย่างที่ไม่ใช้เทคนิคพิเศษใด ๆ เช่นค่า "ล่าง" หรือประเภทข้อมูลที่ไม่มีที่สิ้นสุดหรืออะไรแบบนั้น

ตัวสร้างประเภทเมื่อไม่สามารถมีอินสแตนซ์ของคลาสชนิดได้เมื่อใด

โดยทั่วไปมีสองเหตุผลว่าทำไมตัวสร้างประเภทไม่สามารถมีอินสแตนซ์ของคลาสชนิดที่แน่นอน:

  1. ไม่สามารถใช้ลายเซ็นประเภทของวิธีการที่จำเป็นจากคลาสประเภท
  2. สามารถใช้ลายเซ็นประเภท แต่ไม่สามารถปฏิบัติตามกฎหมายที่ต้องการได้

ตัวอย่างของชนิดแรกนั้นง่ายกว่าของชนิดที่สองเพราะสำหรับชนิดแรกเราเพียงแค่ต้องตรวจสอบว่าสามารถใช้ฟังก์ชันที่มีลายเซ็นประเภทที่กำหนดหรือไม่สำหรับประเภทที่สองเราต้องพิสูจน์ว่าไม่มีการใช้งาน อาจเป็นไปตามกฎหมาย

ตัวอย่างที่เฉพาะเจาะจง

  • ตัวสร้างประเภทที่ไม่สามารถมีอินสแตนซ์ของ functor ได้เนื่องจากไม่สามารถใช้ประเภทได้:

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

นี่คือ contrafunctor ไม่ใช่ functor ที่เกี่ยวกับพารามิเตอร์ type aเนื่องจากaอยู่ในตำแหน่งที่แตกต่างกัน (a -> b) -> F z a -> F z bมันเป็นไปไม่ได้ที่จะใช้ฟังก์ชั่นที่มีลายเซ็นประเภท

  • คอนสตรัคเตอร์ประเภทที่ไม่ใช่ functor ที่ชอบด้วยกฎหมายถึงแม้ว่าลายเซ็นต์ประเภทของfmapสามารถนำไปใช้ได้:

    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!

ตัวอย่างที่น่าสงสัยของตัวอย่างนี้คือเราสามารถใช้งานfmapประเภทที่ถูกต้องได้แม้ว่าFจะไม่สามารถเป็นนักแสดงเพราะใช้aในตำแหน่งที่แตกต่างกัน ดังนั้นการดำเนินการตามที่fmapแสดงด้านบนนี้ทำให้เข้าใจผิด - แม้ว่าจะมีลายเซ็นประเภทที่ถูกต้อง (ฉันเชื่อว่านี่เป็นเพียงการดำเนินการที่เป็นไปได้ของลายเซ็นประเภทนั้น) กฎหมาย functor ไม่พอใจ ยกตัวอย่างเช่นfmap ididเพราะlet (Q(f,_)) = fmap id (Q(read,"123")) in f "456"เป็น123แต่เป็นlet (Q(f,_)) = id (Q(read,"123")) in f "456"456

ในความเป็นจริงFมันเป็นเพียงนักพรต - มันไม่ใช่ functor หรือ contrafunctor

  • นักกฎหมายที่ไม่ได้ใช้งานเพราะไม่สามารถใช้ลายเซ็นประเภทของpure: นำนักเขียน monad (a, w)และลบข้อ จำกัด ที่wควรจะเป็น monoid แล้วมันเป็นไปไม่ได้ที่จะสร้างค่าของชนิดออกจาก(a, w)a

  • นักเขียนที่ไม่ได้สมัครเนื่องจากลายเซ็นประเภทของ<*>ไม่สามารถใช้งานได้: data F a = Either (Int -> a) (String -> a).

  • functor ที่ไม่ถูกต้องตามกฎหมายถึงแม้ว่าวิธีการเรียนประเภทสามารถนำมาใช้:

    data P a = P ((a -> Int) -> Maybe a)

ตัวสร้างประเภทPเป็น functor เนื่องจากใช้aเฉพาะในตำแหน่ง covariant

instance Functor P where
   fmap :: (a -> b) -> P a -> P b
   fmap fab (P pa) = P (\q -> fmap fab $ pa (q . fab))

การนำไปใช้ที่เป็นไปได้ของลายเซ็นประเภท<*>คือฟังก์ชันที่ส่งคืนเสมอNothing:

 (<*>) :: P (a -> b) -> P a -> P b
 (P pfab) <*> (P pa) = \_ -> Nothing  -- fails the laws!

แต่การดำเนินการนี้ไม่เป็นไปตามกฎหมายว่าด้วยตัวตนของผู้ปฏิบัติงาน

  • นักแสดงที่Applicativeไม่ได้เป็นMonadเพราะลายเซ็นประเภทของbindไม่สามารถใช้งานได้

ฉันไม่รู้ตัวอย่างเช่น!

  • นักแสดงที่Applicativeไม่ได้เป็นMonadเพราะกฎหมายไม่สามารถสร้างความพึงพอใจแม้ว่าลายเซ็นประเภทของbindสามารถนำมาใช้

ตัวอย่างนี้ได้สร้างการสนทนาค่อนข้างน้อยดังนั้นจึงปลอดภัยที่จะกล่าวว่าการพิสูจน์ตัวอย่างนี้ถูกต้องไม่ใช่เรื่องง่าย แต่หลายคนได้ตรวจสอบเรื่องนี้อย่างอิสระด้วยวิธีการที่แตกต่างกัน ดูIs` data PoE a = Empty | จับคู่ aa` a monad ไหม? สำหรับการสนทนาเพิ่มเติม

 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

ค่อนข้างยุ่งยากที่จะพิสูจน์ว่าไม่มีMonadตัวอย่างที่ชอบด้วยกฎหมาย เหตุผลในการทำงานที่ไม่เป็นเอกคือว่าไม่มีวิธีธรรมชาติของการดำเนินการbindเมื่อมีฟังก์ชั่นf :: a -> B bจะกลับมาNothingหรือค่าที่แตกต่างกันของJusta

บางทีมันก็เป็นที่ชัดเจนที่จะต้องพิจารณาMaybe (a, a, a)ซึ่งยังไม่ monad และจะพยายามดำเนินjoinการที่ 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.

ในกรณีที่ระบุโดย???ดูเหมือนว่าชัดเจนว่าเราไม่สามารถผลิตJust (z1, z2, z3)ในลักษณะที่เหมาะสมและสมมาตรใด ๆ aหกออกมาจากค่าที่แตกต่างจากประเภท แน่นอนว่าเราสามารถเลือกเซตย่อยทั้งหกของค่านิยมเหล่านี้ได้ตัวอย่างเช่นใช้เวลาไม่ว่างก่อนเสมอMaybe- แต่สิ่งนี้จะไม่เป็นไปตามกฎหมายของ monad การกลับมาNothingจะไม่เป็นไปตามกฎหมาย

  • โครงสร้างข้อมูลที่มีลักษณะคล้ายต้นไม้ที่ไม่ได้เป็น monadแม้ว่าจะมีการเชื่อมโยงกันbindแต่ก็ไม่สามารถใช้กฎหมายตัวตนได้

monad เหมือนต้นไม้ทั่วไป (หรือ "ต้นไม้ที่มีสาขาที่มีรูปทรง functor") ถูกกำหนดเป็น

 data Tr f a = Leaf a | Branch (f (Tr f a))

นี่คือ monad ฟรีผ่าน ffunctor รูปร่างของข้อมูลเป็นต้นไม้ที่แต่ละจุดสาขาเป็น "functor-ful" ของ subtrees type f a = (a, a)ต้นไม้ไบนารีมาตรฐานจะได้รับกับ

ถ้าเราปรับเปลี่ยนโครงสร้างข้อมูลนี้โดยการยังใบในรูปของ functor ที่fเราได้รับสิ่งที่ผมเรียก "semimonad" - มันมีbindที่ตรงกับ naturality และกฎหมายที่เชื่อมโยงกัน แต่ของpureวิธีการล้มเหลวหนึ่งในกฎหมายเอกลักษณ์ "Semimonads เป็นกลุ่มย่อยในหมวดหมู่ของ endofunctors ปัญหาคืออะไร" Bindนี่คือระดับประเภท

เพื่อความเรียบง่ายฉันกำหนดjoinวิธีการแทน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)

การปลูกถ่ายอวัยวะสาขาเป็นมาตรฐาน Branchแต่การปลูกถ่ายอวัยวะใบที่ไม่ได้มาตรฐานและผลิต นี่ไม่ใช่ปัญหาของกฎหมายการเชื่อมโยง แต่เป็นการทำลายหนึ่งในตัวตนของกฎหมาย

พหุนามประเภทใดมีอินสแตนซ์ monad

ทั้ง functors Maybe (a, a)และMaybe (a, a, a)สามารถให้ถูกต้องตามกฎหมายเช่นแม้ว่าพวกเขาจะเห็นได้ชัดว่าMonadApplicative

ฟังก์ชั่นเหล่านี้ไม่มีลูกเล่น - ไม่Voidหรือbottomที่ใดก็ได้ไม่มีความขี้เกียจ / ความเข้มงวดไม่มีโครงสร้างที่ไม่มีที่สิ้นสุดและไม่มีข้อ จำกัด ประเภทคลาส Applicativeเช่นเป็นมาตรฐานสมบูรณ์ ฟังก์ชั่นreturnและbindสามารถนำมาใช้สำหรับฟังก์ชั่นเหล่านี้ แต่จะไม่เป็นไปตามกฎหมายของ monad กล่าวอีกนัยหนึ่งฟังก์ชั่นเหล่านี้ไม่ใช่ monads เพราะโครงสร้างที่เฉพาะเจาะจงหายไป (แต่มันไม่ง่ายที่จะเข้าใจสิ่งที่ขาดหายไป) ตัวอย่างเช่นการเปลี่ยนแปลงเล็กน้อยใน functor สามารถทำให้มันกลายเป็น monad: data Maybe a = Nothing | Just aเป็น monad นักแสดงที่คล้ายกันอีกคนdata P12 a = Either a (a, a)ก็เป็นพระ

การก่อสร้างสำหรับพระพหุนาม

โดยทั่วไปแล้วนี่คือสิ่งปลูกสร้างบางอย่างที่สร้างMonadประเภทพหุนามที่ชอบด้วยกฎหมาย ในสิ่งปลูกสร้างเหล่านี้ทั้งหมดMคือ monad:

  1. type M a = Either c (w, a)อยู่ที่ไหนwmonoid
  2. type M a = m (Either c (w, a))อยู่ที่ไหนmmonad และwเป็น monoid ใด ๆ
  3. type M a = (m1 a, m2 a)ที่ไหนm1และm2เป็นพระใด ๆ
  4. type M a = Either a (m a)ที่ไหนmmonad

การก่อสร้างแรกคือการก่อสร้างที่สองคือWriterT w (Either c) WriterT w (EitherT c m)การสร้างที่สามเป็นผลิตภัณฑ์ที่มีส่วนประกอบของ monads: pure @Mถูกกำหนดให้เป็นผลิตภัณฑ์ที่ส่วนประกอบของpure @m1และpure @m2และjoin @Mถูกกำหนดโดยการละเว้นข้อมูลข้ามผลิตภัณฑ์ (เช่นm1 (m1 a, m2 a)แมปไปm1 (m1 a)โดยการละเว้นส่วนที่สองของ tuple):

 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))

การก่อสร้างที่สี่หมายถึง

 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

ฉันตรวจสอบแล้วว่าสิ่งปลูกสร้างทั้งสี่นี้สร้างพระที่ชอบด้วยกฎหมาย

ฉันคาดเดาว่าไม่มีสิ่งปลูกสร้างอื่นสำหรับพระพหุนาม ตัวอย่างเช่น functor Maybe (Either (a, a) (a, a, a, a))ไม่ได้รับจากสิ่งปลูกสร้างเหล่านี้และดังนั้นจึงไม่ใช่ monadic แต่Either (a, a) (a, a, a)เป็นเอกเพราะมันเป็น isomorphic กับผลิตภัณฑ์ของสาม monads a, และa Maybe aนอกจากนี้ยังEither (a,a) (a,a,a,a)เป็นเอกเพราะมันเป็น isomorphic กับผลิตภัณฑ์ของและaEither a (a, a, a)

สี่การก่อสร้างที่แสดงข้างต้นจะช่วยให้เราเพื่อให้ได้ผลรวมของจำนวนของผลิตภัณฑ์ของจำนวนใด ๆa's เช่นEither (Either (a, a) (a, a, a, a)) (a, a, a, a, a))และอื่น ๆ ตัวสร้างประเภทดังกล่าวทั้งหมดจะมีMonadอินสแตนซ์(อย่างน้อยหนึ่ง)

แน่นอนว่ายังคงต้องมีกรณีการใช้งานใดบ้างสำหรับพระสงฆ์เหล่านี้ ปัญหาอีกประการคือMonadอินสแตนซ์ที่ได้จากการสร้าง 1-4 นั้นโดยทั่วไปไม่ซ้ำกัน ตัวอย่างเช่นพิมพ์สร้างtype F a = Either a (a, a)จะได้รับMonadเช่นในสองวิธีโดยการก่อสร้าง 4 ใช้ monad (a, a)และโดยการก่อสร้าง 3 Either a (a, a) = (a, Maybe a)ใช้ประเภทมอร์ฟ การค้นหากรณีใช้งานสำหรับการนำไปใช้งานเหล่านี้ไม่ชัดเจนในทันที

คำถามยังคงอยู่ - เนื่องจากประเภทข้อมูลพหุนามโดยพลการวิธีการรับรู้ว่ามันมีMonadตัวอย่าง ฉันไม่รู้วิธีที่จะพิสูจน์ว่าไม่มีสิ่งปลูกสร้างอื่นสำหรับพระพหุนาม ฉันไม่คิดว่ามีทฤษฎีใดที่จะตอบคำถามนี้ได้


ฉันคิดว่าB เป็น monad คุณสามารถยกตัวอย่างการโยงให้กับการผูกนี้ได้Pair x y >>= f = case (f x, f y) of (Pair x' _,Pair _ y') -> Pair x' y' ; _ -> Emptyหรือไม่?
Franky

@Franky associativity ล้มเหลวด้วยคำนิยามนี้เมื่อคุณเลือกที่fดังกล่าวว่าf xเป็นEmptyแต่f yเป็นและในขั้นตอนต่อไปทั้งสองPair Pairฉันตรวจสอบด้วยมือว่ากฎหมายไม่ได้ถือปฏิบัตินี้หรือเพื่อการดำเนินการอื่น ๆ แต่มันเป็นงานที่ต้องทำ ฉันหวังว่าจะมีวิธีที่ง่ายกว่าในการหาสิ่งนี้!
winitzki

1
@Turion อาร์กิวเมนต์นั้นไม่มีผลMaybeเนื่องจากMaybeไม่มีค่าต่าง ๆaที่ต้องกังวล
Daniel Wagner

1
@Turion ฉันได้พิสูจน์โดยการคำนวณสองหน้า; ข้อโต้แย้งเกี่ยวกับ "วิธีธรรมชาติ" เป็นเพียงคำอธิบายแบบฮิวริสติก Monadตัวอย่างประกอบด้วยฟังก์ชั่นreturnและbindว่ากฎหมายความพึงพอใจ มีการนำไปปฏิบัติสองแบบreturnและการประยุกต์ใช้ 25 แบบbindที่เหมาะกับประเภทที่ต้องการ คุณสามารถแสดงโดยการคำนวณโดยตรงว่าไม่มีการดำเนินการตามกฎหมาย เพื่อลดปริมาณงานที่ต้องทำฉันใช้joinแทนbindและใช้กฎหมายตัวตนก่อน แต่มันก็เป็นงานที่ค่อนข้างยุติธรรม
winitzki

1
@duplode ไม่ฉันไม่คิดว่าTraversableจำเป็น m (Either a (m a))จะถูกเปลี่ยนโดยใช้เข้าpure @m m (Either (m a) (m a))แล้วนิด ๆและเราสามารถใช้Either (m a) (m a) -> m a join @mนั่นคือการดำเนินการที่ฉันตรวจสอบกฎหมาย
winitzki
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.