简体   繁体   English

派生函子实例,而不是最后一个类型参数

[英]Deriving functor instance, not on last type argument

Related to this question I asked earlier today. 我今天早些时候问过这个问题

I have an AST data type with a large number of cases, which is parameterized by an "annotation" type 我有一个包含大量案例的AST数据类型,它通过“注释”类型进行参数化

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

I've got concrete instances for def and var, say Def and Var . DefVar说我有def和var的具体实例。

What I want is to automatically derive fmap which operates as a functor on the first argument. 我想要的是自动派生fmap ,它在第一个参数上作为fmap函数运行。 I want to derive a function that looks like this: 我想派生一个如下所示的函数:

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

When I use normal fmap , I get a compiler message that indicates fmap is trying to apply its function to the last type argument, not the first. 当我使用普通的fmap ,我得到一条编译器消息,指示fmap正在尝试将其函数应用于最后一个类型参数,而不是第一个。

Is there a way I can derive the function as described, without writing a bunch of boilerplate? 有没有一种方法可以导出所描述的功能,而无需编写一堆样板? I tried doing this: 我试过这样做:

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

But I get the following error: 但是我收到以下错误:

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

I'm working with someone else's code base, so it would be ideal if I don't have to switch the order of the type arguments everywhere. 我正在与其他人的代码库一起工作,所以如果我不必在任何地方切换类型参数的顺序,那将是理想的。

The short answer is, this isn't possible, because Functor requires that the changing type variable be in the last position. 简短的回答是,这是不可能的,因为Functor要求更改类型变量位于最后位置。 Only type constructors of kind * -> * can have Functor instances, and your Expr doesn't have that kind. 只有类型* -> *类型构造函数可以有Functor实例,而您的Expr没有那种类型。

Do you really need a Functor instance? 你真的需要一个Functor实例吗? If you just want to avoid the boilerplate of writing an fmap-like function, something like SYB is a better solution (but really the boilerplate isn't that bad, and you'd only write it once). 如果你只是想避免编写类似fmap的函数的样板,那么像SYB这样的东西是更好的解决方案(但实际上样板文件并不是那么糟糕,而且你只需要编写一次)。

If you need Functor for some other reason (perhaps you want to use this data structure in some function with a Functor constraint), you'll have to choose whether you want the instance or the type variables in the current order. 如果由于某些其他原因需要Functor (可能您希望在具有Functor约束的某个函数中使用此数据结构),则必须选择是否需要当前顺序中的实例或类型变量。

You can exploit a type synonym for minimizing the changes to the original code: 您可以利用类型同义词来最小化对原始代码的更改:

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

The rest of the code can continue using Expr , unchanged. 其余代码可以继续使用Expr ,不变。 The only exceptions are class instances such as Functor , which as you noticed require a certain order in the parameters. 唯一的例外是类实例,例如Functor ,你注意到它需要参数中的某个顺序。 Hopefully Functor is the only such class you need. 希望Functor是您唯一需要的课程。

The auto-derived fmap function has type 自动派生的fmap函数具有类型

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

which can be written as 可以写成

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM