Преобразовать из типа `T a` в`T b` без шаблона
Итак, у меня есть тип данных AST с большим количеством случаев, который параметризуется типом "аннотации"
data Expr a = Plus a Int Int
| ...
| Times a Int Int
У меня есть типы аннотаций S
а также T
и некоторые функции f :: S -> T
, Я хочу взять Expr S
и преобразовать его в Expr T
используя мое преобразование f
на каждой S
что происходит в пределах значения Expr.
Есть ли способ сделать это с помощью SYB или обобщений и избежать необходимости сопоставления с образцом в каждом случае? Кажется, что это то, для чего это подходит. Я просто недостаточно знаком с SYB, чтобы знать, как это сделать.
2 ответа
Похоже, вы хотите Functor
пример. Это может быть автоматически получено GHC с помощью DeriveFunctor
расширение.
Исходя из вашего последующего вопроса, кажется, что универсальная библиотека больше подходит для вашей ситуации, чем Functor. Я бы порекомендовал просто использовать функцию, указанную на вики-странице SYB:
{-# LANGUAGE DeriveDataTypeable, ScopedTypeVariables, FlexibleContexts #-}
import Data.Generics
import Unsafe.Coerce
newtype C a = C a deriving (Data,Typeable)
fmapData :: forall t a b. (Typeable a, Data (t (C a)), Data (t a)) =>
(a -> b) -> t a -> t b
fmapData f input = uc . everywhere (mkT $ \(x::C a) -> uc (f (uc x)))
$ (uc input :: t (C a))
where uc = unsafeCoerce
Причина доп C
тип, чтобы избежать проблемного углового случая, когда есть вхождения полей того же типа, что и a
(подробнее на вики). Вызывающая сторона fmapData
не нужно когда-либо видеть это.
Эта функция имеет несколько дополнительных требований по сравнению с реальной fmap
: должны быть случаи Typeable
за a
, а также Data
за t a
, В твоем случае t a
является Expr a
Это означает, что вам нужно добавить deriving Data
к определению Expr
, а также есть Data
экземпляр в поле зрения для всего a
вы используете