3

我在为以下代码找到合适的类型约束时遇到问题

{-# 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

4

1 回答 1

5

我假设您正在尝试使用Ghc.Generics. 构造函数、字段和数据类型元数据保存在M1节点中。节点用、或M1标记,以指示它们是否保存数据类型、构造函数或选择器(字段)元数据。DCS

我已经简化了你的ClassandGClass返回最外层的构造函数名称,而不是检查它是否是某个名称。我将其解释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为 and 派生一个实例并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在字典中保存One构造函数的元数据。我们不关心比它更深的东西,因为它M1代表最外层的构造函数。

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

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

这意味着只要有元数据标签的实例,我们就可以GClass简单地为节点实现M1 CConstructorc

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

当我们面临两个构造函数的选择时:+:,如果能确定两个构造函数的最外层构造函数名,就可以确定最外层的构造函数名。

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"
于 2015-01-31T19:43:00.487 回答