Free play, part three
- Magnus Therning
The code in the previous post can do with a bit of cleaning up. I start with introducing a type class for an API that can be run
class RunnableF f where
runOp :: f a -> IO a
and a function that actually runs iteFile
runF :: (RunnableF o) => Free o a -> IO a
= foldFree runOp runF
A coproduct (Sum
) of two runnable APIs can itself be runnable
instance (RunnableF f, RunnableF g) => RunnableF (Sum f g) where
InL op) = runOp op
runOp (InR op) = runOp op runOp (
After this all I have to do is to move the guts of runSimpleF
and runLogging
into implementations for SimpleFil
and LogF
respectively
instance RunnableF SimpleFileF where
LoadFile fp f') = liftM f' $ readFile fp
runOp (SaveFile fp d r) = writeFile fp d >> return r
runOp (
instance RunnableF LogF where
Log s a)= putStrLn s >> return a runOp (
The rest is left as is. Running now looks like this
> :! cat test.txt
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus.
Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec
consectetur ante hendrerit.
> runF $ withSimpleFile (map toUpper) "test.txt"
> :! cat test.txt_new
LOREM IPSUM DOLOR SIT AMET, CONSECTETUR ADIPISCING ELIT. DONEC A DIAM LECTUS.
SED SIT AMET IPSUM MAURIS. MAECENAS CONGUE LIGULA AC QUAM VIVERRA NEC
CONSECTETUR ANTE HENDRERIT.
> runF $ foldFree loggingSimpleFileI $ withSimpleFile (map toLower) "test.txt"
** load file test.txt
** save file test.txt_new
> :! cat test.txt_new
lorem ipsum dolor sit amet, consectetur adipiscing elit. donec a diam lectus.
sed sit amet ipsum mauris. maecenas congue ligula ac quam viverra nec
consectetur ante hendrerit.
What else?
With these changes it becomes slightly esier to create new basic API types. but there are still a few things I think could be useful:
- More generic decorators.
logSimpleFileI
is tied to decorating onlySimpleFileF
. I’m fairly sure this could be dealt with by a type classLoggableF
and have APIs implement it. I also think there’s a rather natural implementation ofLoggableF
forSum f g
. - Combining decorators (and other interpreters). I’m guessing this is what John refers to as “generalizing interpreters.”
- Combining APIs. I’d prefer making small APIs of related operations, but then that also means I need a nice way of combining APIs. I had a go at using
FreeT
but simply gave up on bending the types to my will. In any case I’m not completely sure that a stack of variousFreeT
is a good direction to go.