Điểm bản đồ trong Haskell là gì, khi có fmap?


97

Mọi nơi tôi đã thử sử dụng map, fmapđều hoạt động tốt. Tại sao những người tạo ra Haskell lại cảm thấy cần một mapchức năng? Nó không thể chỉ là những gì hiện được gọi là fmapfmapcó thể bị xóa khỏi ngôn ngữ?


11
Tôi nghĩ bạn đang hỏi 'Điểm của fmap trong Haskell là gì'?
zw324,

11
Tôi biết vấn đề fmaplà gì. Nó để ánh xạ một chức năng trên một cá thể Functor. Tôi tự hỏi mục đích của chuyên môn là gì map.
Clark Gaebel

Câu trả lời:


95

Tôi muốn đưa ra một câu trả lời để thu hút sự chú ý vào bình luận của Augustss :

Đó không thực sự là cách nó xảy ra. Điều đã xảy ra là loại bản đồ đã được khái quát hóa để bao phủ Functor trong Haskell 1.3. Tức là trong Haskell 1.3 fmap được gọi là bản đồ. Thay đổi này sau đó đã được hoàn nguyên trong Haskell 1.4 và fmap đã được giới thiệu. Lý do cho sự thay đổi này là do sư phạm; khi dạy Haskell cho người mới bắt đầu về loại bản đồ rất chung chung khiến các thông báo lỗi trở nên khó hiểu hơn. Theo tôi đây không phải là cách đúng đắn để giải quyết vấn đề.

Haskell 98 được coi là một bước lùi của một số Haskellers (bao gồm cả tôi), các phiên bản trước đã định nghĩa một thư viện trừu tượng và nhất quán hơn. Ồ tốt.


16
Những bước lùi này có được thu thập và ghi lại ở bất cứ đâu không? Sẽ rất thú vị khi xem những gì khác được coi là một bước lùi và nếu có những giải pháp tốt hơn cho chúng.
Davorak

3
map and fmapđã có từ lâu - nó đã được hâm nóng lại trong danh sách gửi thư hàng đầu của Haskell vào tháng 8 năm 2006 - haskell.org/pipermail/haskell-prime/2006-August/thread.html . Như một điểm đối lập, tôi thích hiện trạng hơn. Đối với tôi, có vẻ như có giá trị khi có một tập hợp con của Haskell tương ứng với Miranda. Ở Anh, Miranda được sử dụng như một ngôn ngữ giảng dạy cho sinh viên toán học chứ không chỉ sinh viên khoa học máy tính. Nếu vị trí thích hợp đó không bị mất bởi một ngôn ngữ phi chức năng (ví dụ: Mathematica), tôi không thấy Haskell có sự thống nhất maplấp đầy nó.
stephen Tetley

35
Và tôi muốn lưu ý thêm, đối với bất kỳ ai chưa biết, người hùng đó chính là Lennart Augustsson, người vì mọi mục đích thực tế đã là một phần của cộng đồng Haskell từ trước khi Haskell tồn tại, x. Lịch sử của Haskell , vì vậy nhận xét được đề cập không phải là tin đồn cũ!
CA McCann

3
Hiện có trang Nitpicks trên Haskell wiki nơi vấn đề này được đề cập.
Alexey

Thật buồn cười khi quyết định này được thực hiện vì lý do sư phạm, bởi vì tôi đã nhầm lẫn giữa fmap với flatmap khi học Haskell. Họ nên có một nhóm tiêu điểm n00b với nhau. :)
Danny Andrews

27

Trích dẫn từ Functortài liệu tại https://wiki.haskell.org/Typeclassopedia#Functor

Bạn có thể hỏi tại sao chúng ta cần một mapchức năng riêng biệt . Tại sao không loại bỏ mapchức năng chỉ danh sách hiện tại và đổi tên fmapthành map ? Chà, đó là một câu hỏi hay. Lập luận thông thường là ai đó mới học Haskell, khi sử dụng mapkhông chính xác, muốn gặp lỗi về danh sách hơn là về Functor.


1
Điều này rất có ý nghĩa đối với tôi.
hbobenicio

25

Chúng trông giống nhau trên trang ứng dụng nhưng tất nhiên chúng khác nhau. Khi bạn áp dụng một trong hai hàm đó maphoặc fmap, cho một danh sách các giá trị, chúng sẽ tạo ra cùng một kết quả nhưng điều đó không có nghĩa là chúng được sử dụng cho cùng một mục đích.

Chạy một phiên GHCI (Tương tác trình biên dịch Glasgow Haskell) để truy vấn thông tin về hai hàm đó, sau đó xem xét các triển khai của chúng và bạn sẽ phát hiện ra nhiều điểm khác biệt.

bản đồ

Truy vấn GHCI để biết thông tin về map

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

và bạn sẽ thấy nó được định nghĩa là một hàm bậc cao áp dụng cho danh sách các giá trị thuộc bất kỳ loại nào, amang lại danh sách các giá trị thuộc bất kỳ loại nào b. Mặc dù đa hình ( abtrong định nghĩa trên là viết tắt của bất kỳ kiểu nào), maphàm được dự định áp dụng cho danh sách các giá trị chỉ là một kiểu dữ liệu có thể có trong số nhiều kiểu dữ liệu khác trong Haskell. Các mapchức năng không thể được áp dụng cho một cái gì đó mà không phải là một danh sách các giá trị.

Như bạn có thể đọc từ mã nguồn GHC.Base , mapchức năng được triển khai như sau

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

sử dụng so khớp mẫu để kéo phần đầu (cái x) ra khỏi phần đuôi (phần xs) của danh sách, sau đó tạo một danh sách mới bằng cách sử dụng hàm tạo :giá trị (cons) để thêm vào trước f x(đọc nó là "f áp dụng cho x" ) đến đệ quy của mapover tail cho đến khi danh sách trống. Cần lưu ý rằng việc triển khai mapchức năng không dựa vào bất kỳ chức năng nào khác mà chỉ dựa vào chính nó.

fmap

Bây giờ hãy thử truy vấn thông tin về fmapvà bạn sẽ thấy điều gì đó khá khác biệt.

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

Thời gian fmapnày được định nghĩa là một trong những hàm mà việc triển khai phải được cung cấp bởi những kiểu dữ liệu muốn thuộc về Functorlớp kiểu. Điều đó có nghĩa là có thể có nhiều hơn một kiểu dữ liệu, không chỉ kiểu dữ liệu "danh sách giá trị" , có thể cung cấp một triển khai cho fmaphàm. Điều đó có fmapthể áp dụng cho một tập hợp các kiểu dữ liệu lớn hơn nhiều: thực sự là các bộ chức năng!

Như bạn có thể đọc từ mã nguồn GHC.Base , cách triển khai có thể có của fmaphàm là hàm được cung cấp bởi Maybekiểu dữ liệu:

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

và một cách triển khai khả thi khác là cách được cung cấp bởi kiểu dữ liệu 2 tuple

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

và một cách triển khai khả thi khác là kiểu được cung cấp bởi kiểu dữ liệu danh sách (tất nhiên!):

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

mà dựa vào mapchức năng.

Phần kết luận

Các mapchức năng có thể được áp dụng cho không có gì hơn danh sách các giá trị (trong đó giá trị của bất kỳ loại) trong khi các fmapchức năng có thể được áp dụng nhiều hơn nữa các kiểu dữ liệu: tất cả những người thuộc tầng lớp functor (ví dụ maybes, tuples, danh sách, vv ). Vì kiểu dữ liệu "danh sách các giá trị" cũng là một hàm (vì nó cung cấp một triển khai cho nó) nên fmapcó thể được áp dụng cho cũng tạo ra kết quả giống như vậy map.

map  (+3) [1..5]
fmap (+3) (Just 15)
fmap (+3) (5, 7)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.