diff options
| author | Gregor Kleen <gkleen@yggdrasil.li> | 2016-01-25 13:26:29 +0000 |
|---|---|---|
| committer | Gregor Kleen <gkleen@yggdrasil.li> | 2016-01-25 13:26:29 +0000 |
| commit | 776e65149576cb7d8d20c12edc57e6b919c366ee (patch) | |
| tree | dfde4c28302ee9899f890d340a6659c54fe033e8 | |
| parent | 8e32c978e06321439d10146fb60c1ecdf08ffdc4 (diff) | |
| download | dirty-haskell.org-776e65149576cb7d8d20c12edc57e6b919c366ee.tar dirty-haskell.org-776e65149576cb7d8d20c12edc57e6b919c366ee.tar.gz dirty-haskell.org-776e65149576cb7d8d20c12edc57e6b919c366ee.tar.bz2 dirty-haskell.org-776e65149576cb7d8d20c12edc57e6b919c366ee.tar.xz dirty-haskell.org-776e65149576cb7d8d20c12edc57e6b919c366ee.zip | |
Thermoprint 5
| -rw-r--r-- | provider/posts/thermoprint-5.md | 70 |
1 files changed, 70 insertions, 0 deletions
diff --git a/provider/posts/thermoprint-5.md b/provider/posts/thermoprint-5.md new file mode 100644 index 0000000..cb9db47 --- /dev/null +++ b/provider/posts/thermoprint-5.md | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | --- | ||
| 2 | title: Building an Extensible Framework for Specifying Compile-Time Configuration using Universal Quantification | ||
| 3 | tags: Thermoprint | ||
| 4 | published: 2016-01-24 | ||
| 5 | --- | ||
| 6 | |||
| 7 | When I write *Universal Quantification* I mean what is commonly referred to as | ||
| 8 | [existintial quantification](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/data-type-extensions.html#existential-quantification), | ||
| 9 | which I think is a misnomer. To wit: | ||
| 10 | |||
| 11 | $( \exists x \ldotp f(x) ) \to y$ is isomorphic to $\forall x \ldotp (f(x) \to y)$ (I | ||
| 12 | won´t try to back this claim up with actual category theory just now. You might want to | ||
| 13 | nag me occasionally if this bothers you -- I really should invest some more time into | ||
| 14 | category theory). Since haskell does not support `exists` we´re required to use the | ||
| 15 | `forall`-version, which really is universally quantified. | ||
| 16 | |||
| 17 | What we want is to have the user provide us with a set of specifications of how to | ||
| 18 | interact with one printer each. | ||
| 19 | Something like the following: | ||
| 20 | |||
| 21 | ~~~ {.haskell} | ||
| 22 | newtype PrinterMethod = PM { unPM :: Printout -> IO (Maybe PrintingError) } | ||
| 23 | |||
| 24 | data Printer = Printer | ||
| 25 | { print :: PrinterMethod | ||
| 26 | , queue :: TVar Queue | ||
| 27 | } | ||
| 28 | ~~~ | ||
| 29 | |||
| 30 | The first step in refining this is necessitated by having the user provide the | ||
| 31 | [monad-transformer-stack](http://book.realworldhaskell.org/read/monad-transformers.html) | ||
| 32 | to use at compile time. | ||
| 33 | Thus we introduce our first universal quantification (in conjunction with | ||
| 34 | [polymorphic components](https://prime.haskell.org/wiki/PolymorphicComponents)): | ||
| 35 | |||
| 36 | ~~~ {.haskell} | ||
| 37 | newtype PrinterMethod = PM { unPm :: forall m. MonadResource m => Printout -> m (Maybe PrintingError) } | ||
| 38 | ~~~ | ||
| 39 | |||
| 40 | Since we don´t want to *burden* the user with the details of setting up `TVar Queue`{.haskell} we | ||
| 41 | also introduce function to help with that: | ||
| 42 | |||
| 43 | ~~~ {.haskell} | ||
| 44 | printer :: MonadResource m => PrinterMethod -> m Printer | ||
| 45 | printer p = Printer p <$> liftIO (newTVarIO def) | ||
| 46 | ~~~ | ||
| 47 | |||
| 48 | We could at this point provide ways to set up `PrinterMethod`{.haskell}s and have the user | ||
| 49 | provide us with a list of them. | ||
| 50 | |||
| 51 | We, however, have numerous examples of printers which require some setup (such opening a | ||
| 52 | file descriptor). The idiomatic way to handle this is to decorate that setup with some | ||
| 53 | constraints and construct our list of printers in an | ||
| 54 | [`Applicative`{.haskell}](https://hackage.haskell.org/package/base/docs/Control-Applicative.html#t:Applicative) | ||
| 55 | fashion: | ||
| 56 | |||
| 57 | ~~~ {.haskell} | ||
| 58 | printer :: MonadResource m => m PrinterMethod -> m Printer | ||
| 59 | printer p = Printer <$> p <*> liftIO (newTVarIO def) | ||
| 60 | ~~~ | ||
| 61 | |||
| 62 | At this point a toy implementation of a printer we might provide looks like this: | ||
| 63 | |||
| 64 | ~~~ {.haskell} | ||
| 65 | debugPrint :: Applicative m => m PrinterMethod | ||
| 66 | debugPrint = pure . PM $ const return Nothing <=< liftIO . putStrLn . toString | ||
| 67 | |||
| 68 | toString :: Printout -> String | ||
| 69 | toString = undefined | ||
| 70 | ~~~ | ||
