I am looking for the name of this ‘pattern’ where you mirror a Type Class’ methods with an ADT/GADT (whichever fits), then you make that type an instance of the Type Class itself. Because these are 1:1, it is possible to translate back and forth between them.
Anyway, the final result is that you may write abstract programs in terms of the Type Class methods, then either concretize it to an actual implementation, or “AST-ize” with this ‘mirrored type’ in order to manipulate the program.
For example:
class ParserClass p where
string :: String -> p String
eof :: p ()
... etc ...
data ParserData a where
PString :: String -> ParserData String
PEof :: ParserData ()
... etc ...
-- I call this the "AST implementation"
instance ParserClass ParserData where
string = PString
eof = PEof
... etc ...
-- can also give a concrete implementation
instance ParserClass Parsec where -- I know, not accurate types
... bla ..
What I’d do with this is:
-- then I can write an abstract program
data JSON = ...
json :: ParserClass p => p JSON
json = ...
-- and I can observe 'the AST of the program'
-- (in ghci)
>>> json :: ParserData JSON
PString "{" ...
-- I can write an optimizer
optimize :: ParserData a -> ParserData a
optimize = ...
-- because the GADT is 1:1 with the class, I can go from data to class
dataToClass :: ParserClass p => ParserData a -> p a
dataToClass p =
case p of
PString s -> string s
... etc ...
-- then I can optimize the JSON parser and turn it into a Parsec parser
optimizedParsecJson :: Parsec JSON
optimizedParsecJson = dataToClass (optimize json)