2014-12-28 61 views
3

this question相关我今天早些时候问过。派生函数实例,而不是最后一个参数

我有大量的案例,这是由“注释”型

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

我有具体事例为高清和VAR参数的AST数据类型,说DefVar

我想要的是自动派生fmap作为第一个参数仿函数。我想获得一个功能,看起来像这样:

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

当我正常使用fmap,我收到表示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 

我与别人的代码基础工作,所以这将是理想的,如果我没有切换无处不在的类型参数的顺序。

+1

,可以定义不同的,相同类型,在该类型参数你可以做一个丑陋的黑客正确的顺序,为其派生Functor实例,然后在其上使用'unsafeCoerce'。这真的很糟糕,我不推荐它。最简单的选择是推出自己的派生功能;或者干脆重构现有的代码。如果您不介意拉大依赖性,请查看[derive](http://hackage.haskell.org/package/derive-2.5.5/docs/Data-Derive-Functor.html)包。 – user2407038

+2

'Expr ann def'不能有'Functor'实例,除非'var'是用'fmap'改变的类型变量。 – Cirdec

+1

对于hask包来说,没有'DeriveFunctor'类型的功能是很遗憾的。 'hask'包提供了一个polykinded'Functor'类的类,它可以通过类型变量比第一个变量更深(只要所有类型变量右边有'Functor'实例也是如此,在这种情况下,这听起来像是微不足道的)。 –

回答

3

简而言之,这是不可能的,因为Functor要求更改类型变量处于最后位置。只有类型为* -> *的构造函数可以有Functor实例,并且您的Expr不具有那种类型。

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

如果您需要Functor其他一些原因(也许你想在一些函数中使用这种数据结构与Functor约束),你必须选择是否希望实例或当前订单的类型变量。

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 
+1

不幸的是,这并不像它看起来那么有用。类型同义词不能部分应用,所以你永远无法在任何地方使用'Expr ann def''(可能除了'LiberalTypeSynonyms'扩展名,但仍然有限制)。可以使用新类型,但仍不能解决OP的主要问题,即希望获得类似fmap的函数而不必手动编写它。 –

相关问题