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)?
9
A
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.
GHC có lấy trường hợp của 'Generic1' tự động không? –
@ 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
@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. –