Reputation: 1228
I'm trying to do some shallow printing on datatype names but I'm not sure how to do this with Generics-SOP.
For the cases where I have a List of my newtype, I can print out what I need fairly easily:
class GTypeName f where
gtypeName :: g f -> String
instance (HasDatatypeInfo a) => GTypeName (SOP I ('[ '[], '[a, [a]] ])) where
gtypeName _ = "(Array " ++ name ++ ")"
where
name = datatypeName $ datatypeInfo (Proxy @ a)
As you can see, I'm matching on the List kind and using the inner a
's Datatype Info to get the name out. But I don't know how to handle the case where I want to get the actual top level constructor name itself.
In GHC Generics I did the following:
instance (GTypeName f) => GTypeName (D1 m f) where -- peer inside
gtypeName _ = gtypeName (Proxy @ f)
-- go into the Cons constructed type
instance {-# OVERLAPPING #-} (GTypeName f) => GTypeName (C1 ('MetaCons ":" g s) f) where
gtypeName _ = gtypeName (Proxy @ f)
-- return the constructor name here
instance (KnownSymbol n) => GTypeName (C1 ('MetaCons n g s) f) where
gtypeName _ = symbolVal (Proxy @ n)
-- take the left side, as this is the result of a type product from Cons
instance (GTypeName a) => GTypeName (a :*: b) where
gtypeName _ =
gtypeName (Proxy @ a)
-- match on array and take it out here
instance (GTypeName b) => GTypeName ((C1 ('MetaCons "[]" g s) f) :+: b) where
gtypeName _ = "(Array " ++ gtypeName (Proxy @ b) ++ ")"
This is ultimately used with a newtype and some data types like so:
newtype Status = Status Text
newtype OpenRequest = OpenRequest
{ path :: Path
}
data Route req res = Route
{ method :: Method
, url :: Url
}
open :: Route OpenRequest Success
open = Route {method = POST, url = Url "/api/open"}
Upvotes: 4
Views: 115
Reputation: 19657
You can get the top-level constructor name of a value using generics-sop as follows:
constructor ::
forall a .
(Generic a, HasDatatypeInfo a) => a -> String
constructor a =
hcollapse $
hzipWith
(\ con _args -> K (constructorName con))
(constructorInfo (datatypeInfo (Proxy @a)))
(unSOP $ from a)
Here, constructorInfo ...
gives you a product containing all constructor names of the datatype in question. the hzipWith
call then selects the constructor that the given value a
belongs to.
Examples:
GHCi> constructor True
"True"
GHCi> constructor (Just 3)
"Just"
GHCi> constructor [1,2,3]
":"
Unfortunately, it's not entirely clear to me what you want to do with list types, so I cannot show how to combine this with the code you already have.
Upvotes: 2