2012-05-30 28 views
5

Tôi đang chú ý đến các functors, functors ứng dụng ... Tôi không chắc chắn làm thế nào để có được nơi tôi muốn, nhưng tôi có cảm giác rằng sau các loại sẽ giúp tôi gần gũi hơn.Tôi có thể ánh xạ phần tử đầu tiên của một cặp không có mũi tên không?

Có cách nào đơn giản để thực hiện một số map giống như chỉ áp dụng cho phần tử đầu tiên của bộ 2-bit không? Lấy first từ Control.Arrow và sử dụng Arrow (->), điều này làm các trick độc đáo:

map . first :: (b -> c) -> [(b, d)] -> [(c, d)] 

mối quan tâm duy nhất của tôi là tôi vẫn chưa đạt được một trực giác thực sự cho mũi tên, và vì vậy tôi có thể sẽ tìm thấy bản thân mình trong nước sâu sớm hay sau này nếu tôi tiếp tục. Thêm vào đó, điều này có vẻ là một trường hợp khá thuận tiện mà không thể được khái quát hóa.

Tôi có thể nhận được cùng chức năng bằng cách sử dụng một cái gì đó từ các thư viện, monads hay bất cứ điều gì khác, trong khi nhận được trái tim của những gì tôi muốn? Tôi đang đùa giỡn với

\f -> map (f `on` fst) 

giống như ý tưởng nhưng không thể đến đó được.

+11

Nếu sử dụng một sự trừu tượng bạn không hiểu làm cho bạn không thoải mái, có * hoàn toàn không có gì * sai với việc sử dụng bản tóm tắt ít hơn bản đồ (\ (a, b) -> (fa, b)) '. –

+0

Cảm ơn tất cả các đề xuất!Điều này đã thực sự khai sáng. Đã chấp nhận câu trả lời được bầu chọn hàng đầu của bạn. – Ashe

+0

Kiểm tra http://stackoverflow.com/questions/413930 nếu bạn cảm thấy không thoải mái với bản đồ. đầu tiên'. – sdcvvc

Trả lời

9

Mũi tên có bộ phối hợp tốt để hoạt động trên bộ dữ liệu. Bạn gần như có thể nghĩ về chúng như là các chức năng của bộ dữ liệu còn thiếu!

Vì vậy, ví dụ:

> :t \f -> map (f *** id) 
    :: (b -> c) -> [(b, c')] -> [(c, c')] 

là một cách hữu ích để ánh xạ qua thành phần đầu tiên.

+2

Bạn cũng có thể sử dụng (từ Control.Arrow) "map (first f)" để áp dụng hàm cho phần tử đầu tiên của một cặp và tương tự "map (second f)" để áp dụng nó cho phần tử thứ hai. – ozataman

5

Một sự trừu tượng khác có thể thực hiện được loại điều này là một bifunctor. Edward Kmett có một gói gọi là bifunctors. Data.Bifunctor có một loại lớp cho chính xác chức năng này và nó bao gồm trong ví dụ cho 2-tuples.

1

Vâng, có gói BiFunctor.

Hoặc bạn có thể sử dụng một loại cặp đảo ngược:

data Flip a b = Flip b a 

instance Functor (Flip a) where 
    fmap f (Flip x y) = Flip (f x) y 
2

Vì vậy, bạn đang tìm kiếm một chức năng của loại (a -> b) -> (a,c) -> (b,c). Sẽ thật tuyệt nếu bạn chỉ có thể viết

{-# LANGUAGE TupleSections #-} 
instance Functor (,c) where 
    fmap f (x,c) = (f x, c) 

nhưng tiếc là phần tuple chỉ hoạt động ở cấp giá trị. Tôi không biết nếu có lý do lý thuyết cho điều này; Tôi cho rằng nó làm rối loạn sự thống nhất loại bậc cao hơn.

Hayoo đã đưa ra gói Data.Tuple.HT trong đó chức năng được gọi là mapFst.

Nếu không đi đến bifunctors (ví dụ BiFunctor cho (,) thực sự làm những gì bạn muốn và không có gì nhiều hơn) hoặc mũi tên, không có cách nào để có được những gì bạn muốn, mà tôi biết ít nhất.

2

Tôi nghĩ vấn đề là có quá nhiều cách - Tôi nghĩ rằng tôi sẽ đi với lời khuyên Daniel Wagner - nhưng đây là một trò tiêu khiển của bạn:

{-#LANGUAGE DeriveFunctor, MultiParamTypeClasses #-} 
import Control.Newtype 

newtype P a b = P {p:: (b, a)} deriving (Show,Eq,Ord,Functor) 
instance Newtype (P a b) (b,a) where pack = P; unpack = p 

-- *Main> fmap even ("Hi",4) 
-- ("Hi",True) 
-- *Main> map (fmap even) [("Hi",4),("Bye",5)] 
-- [("Hi",True),("Bye",False)] 
-- *Main> under P (fmap even) (4,"Hi") 
-- (True,"Hi") 
-- *Main> map (under P (fmap even)) [(4,"Hi"),(5,"Bye")] 
-- [(True,"Hi"),(False,"Bye")] 
Các vấn đề liên quan