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


100

ทุกที่ที่ฉันได้พยายามใช้map, fmapได้ทำงานได้เป็นอย่างดี เหตุใดผู้สร้าง Haskell จึงรู้สึกว่าต้องการmapฟังก์ชัน เป็นเพียงสิ่งที่เรียกกันในปัจจุบันfmapและfmapสามารถลบออกจากภาษาได้หรือไม่?


13
ฉันคิดว่าคุณกำลังถามว่า 'ประเด็นของ fmap ใน Haskell' คืออะไร?
zw324

13
ฉันรู้ว่าประเด็นfmapคืออะไร เพื่อแมปฟังก์ชันกับอินสแตนซ์ Functor ฉันสงสัยว่าจุดประสงค์ของความเชี่ยวชาญmapคืออะไร
Clark Gaebel

คำตอบ:


95

ฉันต้องการคำตอบเพื่อดึงดูดความสนใจให้กับความคิดเห็นของ augustss :

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

Haskell 98 ถูกมองว่าเป็นขั้นตอนถอยหลังโดย Haskellers บางคน (รวมถึงฉัน) เวอร์ชันก่อนหน้าได้กำหนดไลบรารีที่เป็นนามธรรมและสอดคล้องกันมากขึ้น โอ้ดี.


16
ขั้นตอนย้อนหลังเหล่านี้รวบรวมและบันทึกไว้ที่ใดหรือไม่ มันน่าสนใจที่จะเห็นว่ามีอะไรอีกบ้างที่ถือเป็นก้าวถอยหลังและหากมีแนวทางแก้ไขที่ดีกว่านี้เช่นกัน
Davorak

3
map and fmapได้รับรอบเป็นเวลานาน - มันก็อุ่นในรายการทาง Haskell-prime ในสิงหาคม 2006 - haskell.org/pipermail/haskell-prime/2006-August/thread.html ในฐานะที่เป็นความแตกต่างฉันชอบสภาพที่เป็นอยู่ สำหรับฉันมันดูมีค่าที่มี Haskell ส่วนย่อยที่สอดคล้องกับ Miranda อย่างคร่าวๆ ในสหราชอาณาจักรมิแรนดาถูกใช้เป็นภาษาในการสอนสำหรับนักเรียนคณิตศาสตร์ไม่ใช่แค่นักเรียนวิทยาศาสตร์คอมพิวเตอร์เท่านั้น หากช่องนั้นยังไม่แพ้ภาษาที่ใช้งานไม่ได้ (เช่น Mathematica) ฉันไม่เห็น Haskell ที่มีการmapเติมแบบรวม
stephen tetley

35
และฉันอยากจะแจ้งให้ทราบต่อไปสำหรับใครก็ตามที่ยังไม่ทราบว่าเดือนสิงหาคมคือ Lennart Augustsson ผู้ซึ่งเป็นส่วนหนึ่งของชุมชน Haskell ตั้งแต่ก่อนที่ Haskell จะมีอยู่จริง cf. ประวัติความเป็นมาของ Haskellดังนั้นความคิดเห็นที่เป็นปัญหาจึงไม่ใช่คำบอกเล่าของมือสอง แต่อย่างใด!
CA McCann

3
ขณะนี้มีหน้า Nitpicksใน Haskell wiki ที่กล่าวถึงปัญหานี้
Alexey

1
เป็นเรื่องตลกที่การตัดสินใจครั้งนี้เกิดขึ้นด้วยเหตุผลทางการสอนเพราะฉันสับสน fmap กับ flatmap ตลอดเวลาที่เรียนรู้ Haskell พวกเขาควรมีกลุ่มโฟกัส n00b ร่วมกัน :)
Danny Andrews

29

อ้างจากFunctorเอกสารที่https://wiki.haskell.org/Typeclassopedia#Functor

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


1
สิ่งนี้สมเหตุสมผลกับฉันมาก
hbobenicio

25

พวกเขามีลักษณะเหมือนกันบนไซต์แอปพลิเคชัน แต่แน่นอนว่าแตกต่างกัน เมื่อคุณใช้ฟังก์ชันทั้งสองนี้mapหรือfmapกับรายการของค่าฟังก์ชันเหล่านี้จะให้ผลลัพธ์เดียวกัน แต่ไม่ได้หมายความว่ามีจุดประสงค์เดียวกัน

เรียกใช้เซสชัน GHCI (Glasgow Haskell Compiler Interactive) เพื่อสอบถามข้อมูลเกี่ยวกับฟังก์ชันทั้งสองนี้จากนั้นดูการนำไปใช้งานและคุณจะพบความแตกต่างมากมาย

แผนที่

สอบถาม GHCI สำหรับข้อมูลเกี่ยวกับ map

Prelude> :info map
map :: (a -> b) -> [a] -> [b]   -- Defined in ‘GHC.Base’

และคุณจะเห็นมันกำหนดเป็นฟังก์ชั่นสูงเพื่อใช้บังคับกับรายการของค่าประเภทใดให้ผลผลิตรายการของค่าประเภทใดabแม้ว่า polymorphic ( aและbในนิยามข้างต้นหมายถึงประเภทใด ๆ ) mapฟังก์ชันนี้มีจุดมุ่งหมายเพื่อนำไปใช้กับรายการค่าซึ่งเป็นประเภทข้อมูลที่เป็นไปได้เพียงประเภทเดียวในหมู่อื่น ๆ ใน Haskell mapฟังก์ชั่นไม่สามารถนำไปใช้กับบางสิ่งบางอย่างซึ่งไม่ได้เป็นรายการค่า

ดังที่คุณสามารถอ่านได้จากซอร์สโค้ดGHC.Basemapฟังก์ชันจะมีการใช้งานดังนี้

map _ []     = []
map f (x:xs) = f x : map f xs

ซึ่งใช้การจับคู่รูปแบบเพื่อดึงส่วนหัว (the x) ออกจากส่วนท้าย (the xs) ของรายการจากนั้นสร้างรายการใหม่โดยใช้ตัว:สร้างค่า (ข้อเสีย) เพื่อนำหน้าf x(อ่านว่า"f ใช้กับ x" ) ไปที่การเรียกซ้ำของmapส่วนหางจนกว่ารายการจะว่างเปล่า เป็นที่น่าสังเกตว่าการใช้งานmapฟังก์ชั่นไม่ได้ขึ้นอยู่กับฟังก์ชั่นอื่นใด แต่เพียงแค่ตัวมันเอง

fmap

ตอนนี้ลองค้นหาข้อมูลเกี่ยวกับfmapและคุณจะเห็นสิ่งที่แตกต่างออกไป

Prelude> :info fmap
class Functor (f :: * -> *) where
  fmap :: (a -> b) -> f a -> f b
  ...
  -- Defined in ‘GHC.Base’

เวลานี้fmapถูกกำหนดให้เป็นหนึ่งในฟังก์ชันที่ต้องมีการใช้งานโดยประเภทข้อมูลเหล่านั้นซึ่งต้องการให้เป็นของFunctorคลาสชนิด นั่นหมายความว่าอาจมีประเภทข้อมูลมากกว่าหนึ่งชนิดไม่เพียง แต่ประเภทข้อมูล"รายการค่า"เท่านั้นที่สามารถจัดเตรียมการใช้งานสำหรับfmapฟังก์ชัน นั่นทำให้fmapสามารถใช้ได้กับชุดข้อมูลที่ใหญ่กว่ามาก: functors แน่นอน!

ดังที่คุณสามารถอ่านได้จากซอร์สโค้ดGHC.Baseการใช้งานfmapฟังก์ชันที่เป็นไปได้คือสิ่งที่จัดเตรียมโดยMaybeประเภทข้อมูล:

instance  Functor Maybe  where
  fmap _ Nothing       = Nothing
  fmap f (Just a)      = Just (f a)

และการใช้งานอื่นที่เป็นไปได้คือสิ่งที่จัดทำโดยประเภทข้อมูล 2 ทูเพิล

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

และการนำไปใช้งานอื่นที่เป็นไปได้คือสิ่งที่จัดทำโดยประเภทข้อมูลรายการ (แน่นอน!):

instance  Functor []  where
  fmap f xs = map f xs

ซึ่งขึ้นอยู่กับmapฟังก์ชัน

สรุป

mapฟังก์ชั่นสามารถนำไปใช้อะไรมากไปกว่ารายการค่า (ที่มีค่าของชนิดใด ๆ ) ในขณะที่fmapฟังก์ชั่นสามารถนำมาใช้ชนิดข้อมูลที่มากขึ้น: ทั้งหมดของผู้ที่อยู่ในชั้นเรียน functor (เช่น maybes, tuples, รายการ, ฯลฯ ). ตั้งแต่"รายการของค่า"ชนิดข้อมูลยังเป็น functor (เพราะมีการดำเนินการมัน) แล้วสามารถนำไปใช้เป็นเช่นเดียวกับการผลิตเดียวกันผลเป็นอย่างมากfmapmap

map  (+3) [1..5]
fmap (+3) (Just 15)
fmap (+3) (5, 7)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.