Нет экземпляра для (Generic (f a)), возникающего в результате использования `from'

У меня проблемы с поиском подходящих ограничений типа для следующего кода

{-# 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.Generics, Метаданные конструктора, поля и типа данных хранятся в M1 узлы. M1 узлы помечены либо D, C, или же S чтобы указать, содержат ли они метаданные типа данных, конструктора или селектора (поля).

Я упростила твой Class а также GClass вернуть внешнее имя конструктора вместо проверки, чтобы увидеть, является ли оно определенным именем. Я переводю 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})}

Внешний M1 является M1 D держа в словаре метаданные для Value тип данных. L1 выбирает первый конструктор, One, Внутренний M1 является M1 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 C узлы, пока есть Constructor экземпляр для тега метаданных c

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"
Другие вопросы по тегам