Звучит как хороший вариант использования для обобщений.
{-# LANGUAGE DeriveGeneric, TypeOperators #-}
import GHC.Generics
Мы хотим реализовать некоторую функцию process
для обобщенного типа c:
data Building
= BRestaurant Restaurant
| BStore Store
| BHouse House
deriving Generic
process :: Building -> String
process = gprocess . from
Для этого, мы определяем следующую gprocess
функцию, где мы заменяем тип Building
некоторыми параметрами f p
:
class GProcess f where
gprocess :: f p -> String
и реализуем его для конструкторов различных типов, найденных в GH C .Generics.
Мы всегда поднимаем его через M1
(этот экземпляр становится менее тривиальным, когда вам нужен доступ к именам конструктора или поля):
instance GProcess f => GProcess (M1 i c f) where
gprocess (M1 x) = gprocess x
Мы заинтересованы в обработке типов сумм, которые представлены используя (:+:)
:
instance (GProcess f, GProcess g) => GProcess (f :+: g) where
gprocess (L1 x) = gprocess x
gprocess (R1 y) = gprocess y
Наконец, мы обрабатываем поля с помощью K1
:
instance Show a => GProcess (K1 i a) where
gprocess (K1 x) = show x
И это все для ADT с одним полем на конструктор.
Полный текст: https://gist.github.com/Lysxia/1dd262acbb72231bd4b02b6a8e2fed19
Для получения дополнительной информации о дженериках: