4

与我今天早些时候提出的这个问题有关。

我有一个包含大量案例的 AST 数据类型,它由“注释”类型参数化

data Expr ann def var = Plus a Int Int
    | ...
    | Times a Int Int
    deriving (Data, Typeable, Functor)

我有 def 和 var 的具体实例,比如Defand Var

我想要的是自动派生fmap哪个作为第一个参数的函子运行。我想派生一个看起来像这样的函数:

fmap :: (a -> b) -> (Expr a Def Var) -> (Expr b Def Var)

当我使用 normalfmap时,我收到一条编译器消息,表明 fmap 正在尝试将其函数应用于最后一个类型参数,而不是第一个。

有没有一种方法可以导出所描述的函数,而无需编写一堆样板文件?我试过这样做:

newtype Expr' a = E (Expr a Def Var)
    deriving (Data, Typeable, Functor)

但我收到以下错误:

  Constructor `E' must use the type variable only as the last argument of a data type

我正在使用其他人的代码库,所以如果我不必在任何地方切换类型参数的顺序,那将是理想的。

4

2 回答 2

4

简短的回答是,这是不可能的,因为Functor要求更改类型变量位于最后一个位置。只有种类的类型构造函数* -> *可以有Functor实例,而你Expr没有那种。

你真的需要一个Functor实例吗?如果您只是想避免编写类似 fmap 的函数的样板,那么像 SYB 之类的东西是一个更好的解决方案(但实际上样板并不是那么糟糕,而且您只需要编写一次)。

如果您Functor出于其他原因需要(也许您想在具有Functor约束的某些函数中使用此数据结构),则必须选择是否需要当前顺序的实例或类型变量。

于 2014-12-28T08:54:06.660 回答
2

您可以利用类型同义词来最小化对原始代码的更改:

data Expr' def var ann = Plus a Int Int   -- change this to Expr', correct order
    | ...
    | Something (Expr ann def var)        -- leave this as it is, with the original order
    deriving (Data, Typeable, Functor)

type Expr ann def var = Expr' def var ann

其余代码可以继续使用Expr, 不变。唯一的例外是类实例,例如Functor,正如您所注意到的,它需要参数中的特定顺序。希望Functor是您唯一需要的此类课程。

自动派生fmap函数具有类型

fmap :: (a -> b) -> Expr' def var a -> Expr' def var b

可以写成

fmap :: (a -> b) -> Expr a def var -> Expr b def var
于 2014-12-28T09:13:10.320 回答