所以,我有一个包含大量案例的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不熟悉,知道具体的方法。
答案 0 :(得分:6)
听起来你想要一个Functor
实例。这可以通过GHC使用DeriveFunctor
扩展名自动导出。
答案 1 :(得分:2)
根据您的后续问题,似乎一个泛型库比Functor更适合您的情况。我建议只使用SYB wiki page上给出的函数:
{-# 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
相同类型的字段(有关wiki的更多详细信息)。 fmapData
的来电者不需要看到它。
与真实fmap
相比,此功能确实有一些额外要求:Typeable
必须有a
,Data
必须有t a
。在您的情况下,t a
为Expr a
,这意味着您需要在deriving Data
的定义中添加Expr
,并拥有Data
适用于您正在使用的a
范围内的实例。