Вы можете сделать это с помощью Data.Typeable
.
Сначала используйте вычисления уровня типа, чтобы получить список имен методов из службы:
type MethodNames s = MapNames s (ServiceMethods s)
type family MapNames s (ms :: [Symbol]) :: [Symbol] where
MapNames s (m ': ms) = MethodName s m ': MapNames s ms
MapNames s '[] = '[]
Затем используйте Data.Typeable
чтобы получить TypeRep
за MethodNames s
за выбранную вами услугу s
. Имена методов могут быть извлечены из TypeRep
. После небольшой проб и ошибок, похоже, сработало следующее.
{-# LANGUAGE DataKinds, FlexibleContexts, KindSignatures, MultiParamTypeClasses,
ScopedTypeVariables, TypeFamilies, TypeOperators #-}
import GHC.TypeLits
import Data.Typeable
data ExampleService = ExampleService {}
class Service s where
type ServiceMethods s :: [Symbol]
class HasMethodImpl s (m :: Symbol) where
type MethodName s m :: Symbol
instance Service ExampleService where
type ServiceMethods ExampleService = '["method1", "method2"]
instance HasMethodImpl ExampleService "method1" where
type MethodName ExampleService "method1" = "Method1"
instance HasMethodImpl ExampleService "method2" where
type MethodName ExampleService "method2" = "Method2"
type MethodNames s = MapNames s (ServiceMethods s)
type family MapNames s (ms :: [Symbol]) :: [Symbol] where
MapNames s (m ': ms) = MethodName s m ': MapNames s ms
MapNames s '[] = '[]
getMethods :: forall s. (Service s, Typeable (MethodNames s)) => s -> [String]
getMethods _ = methods (typeRep (Proxy :: Proxy (MethodNames s)))
where methods :: TypeRep -> [String]
methods rep = case typeRepArgs rep of
[x,xs] -> read (tyConName (typeRepTyCon x)) : methods xs
[] -> []
main = do
print $ getMethods ExampleService
-- output: ["Method1","Method2"]