ทำไมเราถึงมี map, fmap และ liftM?


102
map :: (a -> b) -> [a] -> [b]

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

liftM :: Monad m => (a -> b) -> m a -> m b

ทำไมเราถึงมีฟังก์ชั่นสามอย่างที่ทำหน้าที่เหมือนกัน


32
ประวัติศาสตร์ส่วนใหญ่ fmap แตกต่างจากแผนที่ด้วยเหตุผลด้านการสอน, liftM แตกต่างจาก fmap ด้วยเหตุผลทางประวัติศาสตร์ (กล่าวคือ Functor ไม่ได้เป็น superclass ของ Monad)
luqui

12
โอ้และเพื่อความชัดเจน: พวกเขาไม่ "โดยพื้นฐาน" ทำในสิ่งเดียวกัน ทั้งสองmapและliftMแน่นอนที่สุดควรทำว่าfmapเป็นสิ่งเดียวกับ
CA McCann

2
ในขณะที่fmapและliftMทำสิ่งเดียวกันmapแน่นอนว่าเป็นเพียงกรณีพิเศษของพวกเขานั่นคือสิ่งที่แตกต่างกัน fmap id getLineพิมพ์ได้ดีในขณะที่map id getLineไม่ใช่
Thorsten

คำตอบ:


91

mapมีไว้เพื่อลดความซับซ้อนของการดำเนินการในรายการและด้วยเหตุผลทางประวัติศาสตร์ (ดูจุดของแผนที่ใน Haskell คืออะไรเมื่อมี fmap? )

คุณอาจถามว่าทำไมเราต้องมีฟังก์ชันแผนที่แยกต่างหาก ทำไมไม่ใช้ฟังก์ชั่นแผนที่เฉพาะรายการปัจจุบันแล้วเปลี่ยนชื่อ fmap เป็นแผนที่แทนล่ะ? นั่นเป็นคำถามที่ดี ข้อโต้แย้งตามปกติคือคนที่เพิ่งเรียนรู้ Haskell เมื่อใช้แผนที่ไม่ถูกต้องมักจะเห็นข้อผิดพลาดเกี่ยวกับรายการมากกว่า Functors

- Typeclassopedia , หน้า 20

fmapและliftMมีอยู่เนื่องจาก monads ไม่ได้เป็นคนตลกโดยอัตโนมัติใน Haskell:

ความจริงที่ว่าเรามีทั้ง fmap และ liftM เป็นผลมาจากความโชคร้ายที่คลาสประเภท Monad ไม่ต้องการอินสแตนซ์ Functor แม้ว่าจะพูดในเชิงคณิตศาสตร์ แต่ monad ทุกตัวก็เป็น functor อย่างไรก็ตาม fmap และ liftM นั้นใช้แทนกันได้เนื่องจากเป็นข้อบกพร่อง (ในทางสังคมมากกว่าความรู้สึกทางเทคนิค) สำหรับประเภทใด ๆ ที่จะเป็นอินสแตนซ์ของ Monad โดยไม่ต้องเป็นอินสแตนซ์ของ Functor

- Typeclassopedia , หน้า 33

แก้ไข: ประวัติของ agustuss mapและfmap:

นั่นไม่ใช่สิ่งที่เกิดขึ้นจริง สิ่งที่เกิดขึ้นคือประเภทของแผนที่ถูกกำหนดให้ครอบคลุม Functor ใน Haskell 1.3 กล่าวคือใน Haskell 1.3 fmap ถูกเรียกว่า map จากนั้นการเปลี่ยนแปลงนี้ถูกเปลี่ยนกลับใน Haskell 1.4 และมีการนำ fmap มาใช้ เหตุผลของการเปลี่ยนแปลงนี้คือการสอน เมื่อสอน Haskell ให้กับผู้เริ่มต้นประเภทของแผนที่ทั่วไปทำให้ข้อความแสดงข้อผิดพลาดเข้าใจยากขึ้น ในความคิดของฉันนี่ไม่ใช่วิธีที่ถูกต้องในการแก้ปัญหา

- จุดของแผนที่ใน Haskell คืออะไรเมื่อมี fmap?


13
และจากมุมมองของฉันในฐานะคนที่พบ Haskell เป็นครั้งแรกมานานกว่าทศวรรษหลังจากที่มีการอธิบายการเปลี่ยนแปลงที่ @augustss และได้ใช้เวลาส่วนใหญ่ในการช่วยเหลือผู้ที่กำลังเรียนรู้ภาษาในขณะนี้ยังไม่ชัดเจนเลยว่ามันช่วยได้ด้วยซ้ำ อย่างไรก็ตาม. แน่นอนว่าไม่เพียงพอที่จะชดเชยความซ้ำซ้อนที่ไร้ประโยชน์ (ซึ่งนำไปสู่ผู้คนที่ถามคำถามเช่นนี้) Functorระดับเป็นเรื่องธรรมดาเกินไปที่จะไม่สนใจและเริ่มต้นมักจะสับสนโดยข้อความผิดพลาดต่อไป!
CA McCann

10
เราเอาออกliftMไม่ได้เหรอ? ปล่อยให้รหัสแตกใครสนใจโดยปกติจะใช้เวลาน้อยกว่า 2 วันในการแก้ไขรหัสบน github แล้วอัปโหลดเมื่อถูกแฮ็ก หรือฉันบ้าและบ้า?
Tarrasch

1
@Tarrasch: ไม่ใช่ทุกคนที่ใช้ github ไม่ใช่ทุกแพ็คเกจที่มีประวัติที่ยอดเยี่ยมสำหรับการอัปเดตตรงเวลาและฉันสำหรับคนหนึ่งมักจะใช้liftMในขณะที่ทำบล็อกมากกว่าfmapเพราะมันเข้ากันได้ดีกับเวลาที่ฉันใช้liftM2ฯลฯ เช่นกัน.
ivanm

1
@ L01man คนทำงานนี้; ดูstackoverflow.com/questions/5730270/…และอย่างน้อยสำหรับคลาสตัวเลขมีทางเลือกอื่น: hackage.haskell.org/packages/archive/numeric-prelude/0.3.0.2/…
li.davidm

1
@ L01man ใช่สิ่งนี้จะได้รับการแก้ไขในไม่ช้า applicative Monad เสนอ (AMP) ดูเหมือนว่ามันจะผ่านเข้าไปในรุ่นถัดไปของ Haskell GHC 7.8.3มีแฟล็กใหม่--fwarn-ampเพื่อช่วยอัปเดตโค้ดที่มีอยู่สำหรับการเปลี่ยนแปลง
recursion.ninja
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.