Tại sao chúng ta có map, fmap và 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

Tại sao chúng ta có ba chức năng khác nhau về cơ bản làm cùng một thứ?


32
Lịch sử, chủ yếu. fmap là khác biệt với bản đồ vì lý do sư phạm, liftM biệt từ fmap vì những lý do lịch sử (cụ thể là functor không phải là một lớp cha của Monad)
luqui

12
Ồ, và chỉ vì mục đích rõ ràng: Họ không "về cơ bản" làm điều tương tự. Cả hai mapliftMchắc chắn phải làm chính xác cùng một điều như fmap.
CA McCann

2
Trong khi fmapliftMlàm giống hệt nhau, maptất nhiên chỉ là trường hợp đặc biệt của chúng, tức là có gì đó khác biệt. fmap id getLineđược đánh máy tốt, trong khi map id getLinekhông.
Thorsten

Câu trả lời:


91

maptồn tại để đơn giản hóa các thao tác trên danh sách và vì lý do lịch sử (xem Điểm bản đồ trong Haskell là gì, khi có fmap? ).

Bạn có thể hỏi tại sao chúng ta cần một chức năng bản đồ riêng biệt. Tại sao không loại bỏ chức năng bản đồ chỉ danh sách hiện tại và đổi tên fmap thành bản đồ? 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 bản đồ không chính xác, sẽ thấy lỗi về danh sách hơn là về Functors.

- Từ điển điển hình , trang 20

fmapliftMtồn tại bởi vì các monads không tự động là bộ phân giải trong Haskell:

Việc chúng ta có cả fmap và liftM là một hệ quả không may của thực tế là lớp kiểu Monad không yêu cầu thể hiện Functor, mặc dù về mặt toán học, mọi đơn nguyên đều là một hàm functor. Tuy nhiên, fmap và liftM về cơ bản có thể hoán đổi cho nhau, vì nó là một lỗi (theo nghĩa xã hội chứ không phải là kỹ thuật) đối với bất kỳ loại nào là một bản sao của Monad mà không phải là một bản sao của Functor.

- Từ điển điển hình , trang 33

Chỉnh sửa: lịch sử của agustuss mapfmap:

Đó 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 đề.

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


13
Và, từ góc độ của tôi với tư cách là người lần đầu gặp Haskell hơn một thập kỷ sau khi thay đổi mà @augustss mô tả được thực hiện và đã dành rất nhiều thời gian để giúp đỡ những người đang học ngôn ngữ này, không rõ ràng là nó thậm chí còn giúp dù sao. Chắc chắn là không đủ để bù đắp sự dư thừa vô ích (tự nó dẫn đến việc mọi người đặt câu hỏi như thế này); các Functorlớp là quá phổ biến để bỏ qua, và người mới bắt đầu thường bị nhầm lẫn bởi các thông báo lỗi anyway!
CA McCann

10
Chúng ta không thể xóa liftM? Để code bị phá, ai quan tâm, thường mất chưa đến 2 ngày để code được sửa trên github và sau đó tải lên hackage. Hay tôi đang hoang dại và điên rồ?
Tarrasch

1
@Tarrasch: không phải ai cũng sử dụng github, không phải tất cả các gói đều có hồ sơ theo dõi tuyệt vời để được cập nhật kịp thời và tôi có xu hướng sử dụng liftMtrong khi trong do-block hơn là fmapvì nó phù hợp hơn với khi tôi sử dụng liftM2, v.v. cũng.
ivanm

1
@ L01man mọi người đã làm việc này; thấy stackoverflow.com/questions/5730270/... và ít nhất là cho các lớp học số, có những lựa chọn thay thế: hackage.haskell.org/packages/archive/numeric-prelude/0.3.0.2/...
li.davidm

1
@ L01man Có, điều này sẽ sớm được sửa. Các applicative Monad Proposal (AMP) có vẻ như nó sẽ vượt qua vào phiên bản tiếp theo của Haskell. GHC 7.8.3 có một cờ mới --fwarn-ampđể giúp cập nhật mã hiện có cho quá trình chuyển đổi.
recursion.ninja
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.