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 đó map
hoặ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, a
mang 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 ( a
và b
trong định nghĩa trên là viết tắt của bất kỳ kiểu nào), map
hà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 map
chứ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 , map
chứ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 map
over tail cho đến khi danh sách trống. Cần lưu ý rằng việc triển khai map
chứ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ề fmap
và 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 fmap
nà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ề Functor
lớ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 fmap
hàm. Điều đó có fmap
thể á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 fmap
hàm là hàm được cung cấp bởi Maybe
kiể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 map
chức năng.
Phần kết luận
Các map
chứ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 fmap
chứ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 fmap
có 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)