haskellgeneric-programmingghc-generics

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


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


Solution

  • 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"