i deserialize a data structure from disc in the finally tagless style. i.e.
class SYM repl where
a :: repl
include :: FilePath -> repl
myParser :: SYM r => Parser r
the language i am parsing has include directives.
i am using attoparsec
which is not a monad transformer, so i cannot simply supply a type Loader = FilePath -> IO (Maybe Text)
.
i can write an interpreter with the following SYM
instance, that resolves an include.
instance SYM r => SYM (Loader -> IO (Either String r)) where
include path loader =
maybe (Left "cannot load") (parseOnly myParser) <$> loader path
unfortunately includes in the included file don't get resolved. of course i can resolve them twice to resolve the next layer. but that leads to infinite types if i want to do that for every possible level.
right now i preload all the includes (in a HashMap
) and bind them, so i can pass a FilePath -> Maybe Text
to the parser and resolve includes there, but that is clearly not optimal. (and include is not part of SYM
anymore.)
my question is, how does a finally tagless style deal with that problem?
edit: i published a complete example on lpaste: http://lpaste.net/105182
It was pretty easy in hindsight, but it usually is.
The one-level-only resolving is simply the following.
I.e. it will run
parseOnly myParser :: Either String r
on the (successfully) loaded file.The resolve-everything will just need to select the
SYM (Loader -> IO (Either String r))
instance for themyParser
and add theloader
argument:The crucial step is that it will supply the additional parameter loader to the newly
parseOnly
dSYM
repl, thus selecting the right instance.A complete snippet is in the annotated lambda paste: http://lpaste.net/105182. test it with entering "include include token"