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 ( avà btrong đị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)