Преобразовать из типа `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 вы используете

Другие вопросы по тегам