I'm having issues with finding suitable type constraints for the following code
{-# 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
It fails with
No instance for (Generic (f a)) arising from a use of `from'
Adding the constraint to gname
like this
instance (Generic (f a)) => GClass (f :+: g) where
fails with
Could not deduce (Generic (f a1)) arising from a use of `from'
from the context (Generic (f a))
Edit: The full error message for the full snippet
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
This is with GHC 7.6.3
I presume you are trying to get the constructor names using Ghc.Generics
. Constructor, field, and data type metadata is held in M1
nodes. M1
nodes are tagged with either D
, C
, or S
to indicate whether they hold datatype, constructor, or selector (field) metadata.
I've simplified your Class
and GClass
to return the outermost constructor name instead of checking to see if it is a certain name. I'm interpreting Class
as the class of types whose values have an outermost constructor with a name.
{-# 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
We'd like to be able to derive a Class
instance for Value
and observe that cname One == "One"
and cname Two == "Two"
.
instance Class Value
main = do
print . cname $ One
print . cname $ Two
We need to implement GClass
for three of the representation nodes to be able to do this. The representation for One
is:
> from One
M1 {unM1 = L1 (M1 {unM1 = U1})}
The outer M1
is an M1 D
holding in a dictionary the metadata for the Value
datatype. The L1
is selecting the first constructor, One
. The inner M1
is an M1 C
holding in a dictionary the metadata for the One
constructor. We don't care about anything deeper than it, since that M1
represents the outermost constructor.
The most interesting node is the inner M1 C
which holds the constructor metadata. We can get the constructor name as long as the metadata implements the Constructor
class. The Constructor
class includes conName
which returns the constructor name given an appropriate proxy, and the appropriate proxy type is designed to looks like the type of M1 C
.
conName :: Constructor c => t c (f :: * -> *) a -> [Char]
M1 C c f p
This means we can implement GClass
simply for M1 C
nodes as long as there's a Constructor
instance for the metadata tag c
instance (Constructor c) => GClass (M1 C c f) where
gname = conName
When we are faced with the choice between two constructors, :+:
, we can determine the outermost constructor name if we can determine the outermost constructor name of both constructors.
instance (GClass f, GClass g) => GClass (f :+: g) where
gname (L1 x) = gname x
gname (R1 x) = gname x
When we are dealing with a metadata node for a datatype, M1 D
, we can determine the outermost constructor name when we can determine the outermost constructor name of the representation absent the metadata node.
instance GClass f => GClass (M1 D c f) where
gname (M1 x) = gname x
With these three instances we can run our desired code and see that
> cname One
"One"
> cname Two
"Two"