2013-07-19 24 views
9

Tôi đang cố gắng tìm hiểu GHC Generics. Sau khi xem xét một số ví dụ, tôi muốn cố gắng tạo một trường hợp chung Functor (bỏ qua việc GHC có thể lấy chúng tự động cho tôi). Tuy nhiên, tôi nhận ra tôi không có ý tưởng làm thế nào để làm việc với một loại dữ liệu parametrized với Generics, tất cả các ví dụ tôi đã nhìn thấy là loại *. Điều này có thể, và nếu có, làm thế nào? (Tôi cũng quan tâm đến các khuôn khổ tương tự khác, chẳng hạn như SYB.)Làm thế nào để xây dựng các trường hợp Functor chung sử dụng GHC.Generics (hoặc các khuôn khổ tương tự khác)?

Trả lời

8

Nơi tốt nhất để tìm kiếm nhiều chức năng mẫu bằng cách sử dụng GHC Generics là generic-deriving package. Có một định nghĩa chung của lớp học Functor trong đó. Sao chép (hơi đơn giản) từ Generics.Deriving.Functor:

class GFunctor' f where 
    gmap' :: (a -> b) -> f a -> f b 

instance GFunctor' U1 where 
    gmap' _ U1 = U1 

instance GFunctor' Par1 where 
    gmap' f (Par1 a) = Par1 (f a) 

instance GFunctor' (K1 i c) where 
    gmap' _ (K1 a) = K1 a 

instance (GFunctor f) => GFunctor' (Rec1 f) where 
    gmap' f (Rec1 a) = Rec1 (gmap f a) 

instance (GFunctor' f) => GFunctor' (M1 i c f) where 
    gmap' f (M1 a) = M1 (gmap' f a) 

instance (GFunctor' f, GFunctor' g) => GFunctor' (f :+: g) where 
    gmap' f (L1 a) = L1 (gmap' f a) 
    gmap' f (R1 a) = R1 (gmap' f a) 

instance (GFunctor' f, GFunctor' g) => GFunctor' (f :*: g) where 
    gmap' f (a :*: b) = gmap' f a :*: gmap' f b 

instance (GFunctor f, GFunctor' g) => GFunctor' (f :.: g) where 
    gmap' f (Comp1 x) = Comp1 (gmap (gmap' f) x) 


class GFunctor f where 
    gmap :: (a -> b) -> f a -> f b 
    default gmap :: (Generic1 f, GFunctor' (Rep1 f)) 
       => (a -> b) -> f a -> f b 
    gmap = gmapdefault 

gmapdefault :: (Generic1 f, GFunctor' (Rep1 f)) 
      => (a -> b) -> f a -> f b 
gmapdefault f = to1 . gmap' f . from1 

Để sử dụng này trên một datatype, bạn phải lấy được Generic1 hơn Generic. Sự khác biệt chính của biểu diễn Generic1 là nó sử dụng kiểu dữ liệu Par1 mã hóa vị trí tham số.

3

Có một lớp Generic1 cho các loại dữ liệu loại * -> *. Làm việc với nó chủ yếu giống như với kiểu dữ liệu loại *, ngoại trừ cũng có Par1 cho tham số. Tôi đã sử dụng nó trong ví dụ unfoldable package của tôi.

+0

GHC có lấy trường hợp của 'Generic1' tự động không? –

+1

@ PetrPudlák Không hoàn toàn tự động. Nhưng với phần mở rộng ngôn ngữ 'DeriveGeneric', bạn có thể sử dụng' deriving Generic' cũng như 'deriving Generic1' (nơi sau này chỉ hoạt động cho các kiểu dữ liệu có ít nhất một tham số, tham số cuối cùng thuộc loại' * '). – kosmikus

+0

@kosmikus Cảm ơn bạn. Thật không may cho mục tiêu của tôi tôi muốn làm việc với các loại phức tạp hơn, vì vậy có lẽ tôi sẽ phải sử dụng mẫu Haskell. –