2015-01-31 15 views
3

我在与寻找合适的类型约束以下代码问题而引起的没有实例(通用(FA))从使用'从”

{-# LANGUAGE DeriveGeneriC#-} 
{-# LANGUAGE DefaultSignatures #-} 
{-# LANGUAGE TypeOperators #-} 
{-# LANGUAGE FlexibleContexts #-} 
import GHC.Generics 

data Value = One | Two deriving Generic 

class Class a where 
    cname :: a -> String -> Bool 
    default cname :: (Generic a, GClass (Rep a)) 
       => a -> String -> Bool 
    cname = gname . from 

class GClass f where 
    gname :: f a -> String -> Bool 

instance GClass (f :+: g) where 
    gname (L1 x) s | conName (from x) == s = True 
        | otherwise    = False 
    gname (R1 x) s | conName (from x) == s = True 
        | otherwise    = False 

它失败

No instance for (Generic (f a)) arising from a use of `from' 

添加约束gname这样

instance (Generic (f a)) => GClass (f :+: g) where 

失败

Could not deduce (Generic (f a1)) arising from a use of `from' 
from the context (Generic (f a)) 

编辑:为完整的片段

Generic.hs:19:31: 
    No instance for (Generic (f a)) arising from a use of `from' 
    Possible fix: add an instance declaration for (Generic (f a)) 
    In the first argument of `conName', namely `(from x)' 
    In the first argument of `(==)', namely `conName (from x)' 
    In the expression: conName (from x) == s 

Generic.hs:21:31: 
    No instance for (Generic (g a)) arising from a use of `from' 
    Possible fix: add an instance declaration for (Generic (g a)) 
    In the first argument of `conName', namely `(from x)' 
    In the first argument of `(==)', namely `conName (from x)' 
    In the expression: conName (from x) == s 

这个完整的错误消息是GHC 7.6.3

+1

你确定这就是你与发布代码得到的错误这三个实例?你使用的是什么版本的ghc? 'Class'和'GClass'都可以用ghc 7.8.3编译,但':+:'的'GClass'实例不能编译时会出现不同的错误信息。包含更多的错误信息也很有用;在代码中有三个'from'出现,而来自ghc的完整错误信息会告诉他们哪些没有所需的实例。 – Cirdec 2015-01-31 19:07:02

回答

4

我相信你正在尝试使用Ghc.Generics获得的构造名。构造函数,字段和数据类型元数据保存在M1节点中。 M1节点用D,CS标记以指示它们是否保存数据类型,构造函数或选择器(字段)元数据。

我简化了您的ClassGClass,以返回最外层的构造函数名称,而不是检查它是否是某个名称。我将Class解释为其值具有带名称的最外层构造函数的类的类。

{-# LANGUAGE DeriveGeneriC#-} 
{-# LANGUAGE DefaultSignatures #-} 
{-# LANGUAGE TypeOperators #-} 
{-# LANGUAGE FlexibleContexts #-} 
{-# LANGUAGE FlexibleInstances #-} 
import GHC.Generics 

data Value = One | Two deriving Generic 

class Class a where 
    cname :: a -> String 
    default cname :: (Generic a, GClass (Rep a)) 
       => a -> String 
    cname = gname . from 

class GClass f where 
    gname :: f a -> String 

我们希望能够得到一个Class实例Value并观察cname One == "One"cname Two == "Two"

instance Class Value  

main = do 
    print . cname $ One 
    print . cname $ Two 

我们需要实现GClass三表示节点才能够做到这一点。为One表示是:

> from One 
M1 {unM1 = L1 (M1 {unM1 = U1})} 

M1M1 D在字典保持元数据为Value数据类型。 L1正在选择第一个构造函数One。内部M1M1 C在字典中保存构造函数的元数据。我们不关心比它更深的东西,因为M1代表最外层的构造函数。

最感兴趣的节点是内部的M1 C它包含构造函数元数据。只要元数据实现Constructor类,我们就可以获得构造函数名称。 Constructor类包括conName它返回给定一个适当的代理的构造函数名称,并且适当的代理类型被设计为看起来像M1 C类型。

conName :: Constructor c => t c (f :: * -> *) a -> [Char] 
          M1 C c f   p 

这意味着,只要我们可以实现GClass只是为M1 C节点,因为是为元数据标签c

instance (Constructor c) => GClass (M1 C c f) where 
    gname = conName 

当我们两个构造之间面临选择,:+:一个Constructor例如,我们可以确定最外层的构造函数名称,如果我们可以确定两个构造函数的最外层构造函数名称。

instance (GClass f, GClass g) => GClass (f :+: g) where 
    gname (L1 x) = gname x 
    gname (R1 x) = gname x 

当我们正在处理的数据类型,M1 D元数据节点,我们可以判断最外层构造函数名称时,我们可以确定表示的最外层构造函数名缺席的元数据节点。

instance GClass f => GClass (M1 D c f) where 
    gname (M1 x) = gname x 

随着我们可以运行我们需要的代码,看到

> cname One 
"One" 
> cname Two 
"Two" 
+0

伟大的写作,并正确回答我的问题。谢谢你的帮助! – sgillis 2015-01-31 19:49:06