Mengapa kita memiliki map, fmap dan 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

Mengapa kita memiliki tiga fungsi berbeda yang pada dasarnya melakukan hal yang sama?

fredoverflow
sumber
32
Sebagian besar sejarah. fmap berbeda dari peta karena alasan pedagogis, liftM berbeda dari fmap karena alasan historis (yaitu Functor bukan superclass dari Monad)
luqui
12
Oh, dan hanya untuk memperjelas: Mereka "pada dasarnya" tidak melakukan hal yang sama. Keduanya mapdan liftMpasti harus melakukan hal yang persis sama seperti fmap.
CA McCann
2
Sementara fmapdan liftMmelakukan hal yang persis sama, maptentu saja hanya kasus khusus dari mereka, yaitu sesuatu yang berbeda. fmap id getLinediketik dengan baik, sedangkan map id getLinetidak.
Thorsten

Jawaban:

91

mapada untuk menyederhanakan operasi pada daftar dan untuk alasan historis (lihat Apa gunanya peta di Haskell, bila ada fmap? ).

Anda mungkin bertanya mengapa kami membutuhkan fungsi peta terpisah. Mengapa tidak menyingkirkan saja fungsi peta hanya-daftar saat ini, dan mengganti nama fmap menjadi peta? Nah, itu pertanyaan yang bagus. Argumen yang biasa adalah bahwa seseorang yang baru saja mempelajari Haskell, ketika menggunakan peta secara tidak benar, lebih suka melihat kesalahan tentang daftar daripada tentang Functor.

- Typeclassopedia , halaman 20

fmapdan liftMada karena monad tidak secara otomatis berfungsi di Haskell:

Fakta bahwa kita memiliki fmap dan liftM adalah konsekuensi yang tidak menguntungkan dari fakta bahwa kelas tipe Monad tidak memerlukan turunan Functor, meskipun secara matematis, setiap monad adalah functor. Namun, fmap dan liftM pada dasarnya dapat dipertukarkan, karena ini adalah bug (dalam arti sosial daripada teknis) untuk semua jenis yang menjadi instance Monad tanpa juga menjadi instance Functor.

- Typeclassopedia , halaman 33

Sunting: sejarah agustuss mapdan fmap:

Sebenarnya bukan itu yang terjadi. Yang terjadi adalah tipe peta digeneralisasikan untuk mencakup Functor di Haskell 1.3. Yaitu, di Haskell 1.3 fmap disebut map. Perubahan ini kemudian dikembalikan di Haskell 1.4 dan fmap diperkenalkan. Alasan perubahan ini adalah karena pedagogis; saat mengajari Haskell kepada pemula, tipe peta yang sangat umum membuat pesan kesalahan lebih sulit untuk dipahami. Menurut saya, ini bukan cara yang tepat untuk menyelesaikan masalah.

- Apa gunanya map di Haskell, kalau ada fmap?

li.davidm
sumber
13
Dan, dari sudut pandang saya sebagai seseorang yang pertama kali bertemu Haskell lebih dari satu dekade setelah perubahan yang dijelaskan @augustss dibuat, dan telah menghabiskan banyak waktu untuk membantu orang-orang yang sedang belajar bahasa sekarang, sama sekali tidak jelas bahwa itu bahkan membantu dalam dengan cara apapun. Tentu saja tidak cukup untuk mengimbangi redundansi yang tidak berguna (yang dengan sendirinya menyebabkan orang mengajukan pertanyaan seperti ini); yang Functorkelas terlalu umum untuk mengabaikan, dan pemula sering bingung dengan pesan error pula!
CA McCann
10
Tidak bisakah kita menghapusnya liftM? Biarkan kode rusak, siapa peduli, biasanya butuh waktu kurang dari 2 hari untuk kode diperbaiki di github dan kemudian diunggah di hackage. Atau apakah saya menjadi liar dan gila?
Tarrasch
1
@Tarrasch: tidak semua orang menggunakan github, tidak semua paket memiliki rekam jejak yang bagus untuk diperbarui tepat waktu, dan saya untuk satu cenderung menggunakan liftMsaat berada dalam blok-do daripada fmapkarena itu lebih cocok dengan saat saya menggunakan liftM2, dll. demikian juga.
ivanm
1
@ L01man orang telah mengerjakan ini; lihat stackoverflow.com/questions/5730270/… dan setidaknya untuk kelas numerik, ada alternatif: hackage.haskell.org/packages/archive/numeric-prelude/0.3.0.2/…
li.davidm
1
@ L01man Ya, ini akan segera diperbaiki. The Aplikatif Monad Proposal (AMP) terlihat seperti itu akan masuk ke versi berikutnya dari Haskell. GHC 7.8.3 memiliki bendera baru --fwarn-ampuntuk membantu memperbarui kode yang ada untuk transisi.
recursion.ninja