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 ron the (successfully) loaded file.The resolve-everything will just need to select the
SYM (Loader -> IO (Either String r))instance for themyParserand add theloaderargument:The crucial step is that it will supply the additional parameter loader to the newly
parseOnlydSYMrepl, 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"