Нет экземпляра для (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"