Apa gunanya map di Haskell, kalau ada fmap?

98

Di mana pun saya mencoba menggunakan map, fmaptelah berhasil juga. Mengapa pencipta Haskell merasa membutuhkan suatu mapfungsi? Tidak bisakah itu hanya apa yang saat ini dikenal sebagai fmapdan fmapdapat dihapus dari bahasa?

Clark Gaebel
sumber
11
Saya pikir Anda bertanya 'Apa gunanya fmap di Haskell'?
zw324
12
Saya tahu apa gunanya fmap. Ini untuk memetakan fungsi melalui instance Functor. Saya ingin tahu tentang tujuan dari spesialisasi tersebut map.
Clark Gaebel

Jawaban:

95

Saya ingin membuat jawaban untuk menarik perhatian pada komentar Agustus :

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 para pemula jenis peta yang sangat umum membuat pesan kesalahan lebih sulit untuk dipahami. Menurut saya, ini bukan cara yang tepat untuk menyelesaikan masalah.

Haskell 98 dipandang sebagai langkah mundur oleh beberapa Haskeller (termasuk saya), versi sebelumnya telah mendefinisikan pustaka yang lebih abstrak dan konsisten. Baiklah.

luqui
sumber
16
Apakah langkah mundur ini dikumpulkan dan didokumentasikan di mana saja? Akan menarik untuk melihat apa lagi yang dianggap sebagai langkah mundur dan apakah ada solusi yang lebih baik untuk mereka juga.
Davorak
3
The map and fmaptelah sekitar untuk waktu yang lama - itu dipanaskan ulang di milis Haskell-prime pada bulan Agustus 2006 - haskell.org/pipermail/haskell-prime/2006-August/thread.html . Sebagai pengimbang, saya lebih suka status quo. Bagi saya, tampaknya berharga bahwa ada bagian dari Haskell yang secara kasar mirip dengan Miranda. Di Inggris, Miranda digunakan sebagai bahasa pengajaran untuk siswa matematika, bukan hanya siswa ilmu komputer. Jika ceruk itu belum hilang dari bahasa non-fungsional (misalnya Mathematica), saya tidak melihat Haskell dengan kesatuan yang mapmengisinya.
stephen tetley
35
Dan selanjutnya saya ingin mencatat, bagi siapa pun yang belum tahu, bahwa Agustus adalah Lennart Augustsson, yang untuk semua tujuan praktis telah menjadi bagian dari komunitas Haskell sejak sebelum Haskell ada, lih. A History of Haskell , jadi komentar yang dipermasalahkan bukanlah desas-desus bekas!
CA McCann
3
Sekarang ada halaman Nitpicks di wiki Haskell di mana masalah ini disebutkan.
Alexey
Lucunya keputusan ini dibuat karena alasan pedagogis, karena saya selalu bingung fmap dengan flatmap ketika mempelajari Haskell. Mereka seharusnya memiliki grup fokus n00b bersama-sama. :)
Danny Andrews
27

Mengutip dari Functordokumentasi di https://wiki.haskell.org/Typeclassopedia#Functor

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

Andrei Bozantan
sumber
1
Ini sangat masuk akal bagi saya.
hbobenicio
25

Mereka terlihat sama di situs aplikasi tetapi mereka berbeda, tentunya. Saat Anda menerapkan salah satu dari dua fungsi tersebut, mapatau fmap, ke daftar nilai, keduanya akan menghasilkan hasil yang sama tetapi itu tidak berarti fungsi tersebut dimaksudkan untuk tujuan yang sama.

Jalankan sesi GHCI (Glasgow Haskell Compiler Interactive) untuk meminta informasi tentang kedua fungsi tersebut, kemudian lihat implementasinya dan Anda akan menemukan banyak perbedaan.

peta

Tanya GHCI untuk informasi tentang map

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

dan Anda akan melihatnya ditetapkan sebagai fungsi tingkat tinggi yang berlaku untuk daftar nilai jenis apa pun a menghasilkan daftar nilai jenis apa pun b. Meskipun polimorfik ( adan bdalam definisi di atas berarti semua tipe) mapfungsi ini dimaksudkan untuk diterapkan ke daftar nilai yang hanya merupakan satu tipe data yang mungkin di antara banyak tipe data lainnya di Haskell. The mapFungsi tidak dapat diterapkan untuk sesuatu yang bukan merupakan daftar nilai.

Seperti yang dapat Anda baca dari kode sumber GHC.Base , mapfungsi tersebut diimplementasikan sebagai berikut

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

yang memanfaatkan pencocokan pola untuk menarik kepala (the x) dari ekor (thexs ) dari daftar, kemudian membuat daftar baru dengan menggunakan :konstruktor nilai (kontra) sehingga untuk menambahkan f x(baca sebagai "f diterapkan ke x" ) ke rekursi mapmelewati ekor sampai daftar kosong. Perlu diperhatikan bahwa implementasi mapfungsi tidak bergantung pada fungsi lain tetapi hanya pada dirinya sendiri.

fmap

Sekarang coba untuk menanyakan informasi tentang fmapdan Anda akan melihat sesuatu yang sangat berbeda.

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

Kali fmapini didefinisikan sebagai salah satu fungsi yang implementasinya harus disediakan oleh tipe data yang ingin dimiliki Functorkelas tipe. Artinya, bisa ada lebih dari satu tipe data, tidak hanya "daftar nilai" , yang dapat menyediakan implementasi untuk fmapfungsi tersebut. Itu membuat fmapdapat diterapkan ke kumpulan tipe data yang jauh lebih besar: fungtor memang!

Seperti yang dapat Anda baca dari kode sumber GHC.Base , kemungkinan implementasi fmapfungsi adalah yang disediakan olehMaybe tipe data:

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

dan implementasi lain yang mungkin adalah yang disediakan oleh tipe data 2-tupel

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

dan kemungkinan implementasi lainnya adalah yang disediakan oleh tipe data daftar (tentu saja!):

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

yang bergantung pada mapfungsinya.

Kesimpulan

The mapfungsi dapat diterapkan untuk tidak lebih dari daftar nilai (di mana nilai-nilai dari jenis apa pun) sedangkan fmapfungsi dapat diterapkan tipe data jauh lebih: semua orang yang termasuk kelas functor (misalnya maybes, tupel, daftar, dll ). Karena tipe data "daftar nilai" juga berfungsi (karena menyediakan implementasi untuknya), maka fmapdapat diterapkan juga menghasilkan hasil yang sama seperti map.

map  (+3) [1..5]
fmap (+3) (Just 15)
fmap (+3) (5, 7)
Paolo Angioletti
sumber