summaryrefslogtreecommitdiff
path: root/provider/posts
diff options
context:
space:
mode:
Diffstat (limited to 'provider/posts')
-rw-r--r--provider/posts/thermoprint-5.md70
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---
2title: Building an Extensible Framework for Specifying Compile-Time Configuration using Universal Quantification
3tags: Thermoprint
4published: 2016-01-24
5---
6
7When 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),
9which 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
12won´t try to back this claim up with actual category theory just now. You might want to
13nag me occasionally if this bothers you -- I really should invest some more time into
14category theory). Since haskell does not support `exists` we´re required to use the
15`forall`-version, which really is universally quantified.
16
17What we want is to have the user provide us with a set of specifications of how to
18interact with one printer each.
19Something like the following:
20
21~~~ {.haskell}
22newtype PrinterMethod = PM { unPM :: Printout -> IO (Maybe PrintingError) }
23
24data Printer = Printer
25 { print :: PrinterMethod
26 , queue :: TVar Queue
27 }
28~~~
29
30The 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)
32to use at compile time.
33Thus we introduce our first universal quantification (in conjunction with
34[polymorphic components](https://prime.haskell.org/wiki/PolymorphicComponents)):
35
36~~~ {.haskell}
37newtype PrinterMethod = PM { unPm :: forall m. MonadResource m => Printout -> m (Maybe PrintingError) }
38~~~
39
40Since we don´t want to *burden* the user with the details of setting up `TVar Queue`{.haskell} we
41also introduce function to help with that:
42
43~~~ {.haskell}
44printer :: MonadResource m => PrinterMethod -> m Printer
45printer p = Printer p <$> liftIO (newTVarIO def)
46~~~
47
48We could at this point provide ways to set up `PrinterMethod`{.haskell}s and have the user
49provide us with a list of them.
50
51We, however, have numerous examples of printers which require some setup (such opening a
52file descriptor). The idiomatic way to handle this is to decorate that setup with some
53constraints and construct our list of printers in an
54[`Applicative`{.haskell}](https://hackage.haskell.org/package/base/docs/Control-Applicative.html#t:Applicative)
55fashion:
56
57~~~ {.haskell}
58printer :: MonadResource m => m PrinterMethod -> m Printer
59printer p = Printer <$> p <*> liftIO (newTVarIO def)
60~~~
61
62At this point a toy implementation of a printer we might provide looks like this:
63
64~~~ {.haskell}
65debugPrint :: Applicative m => m PrinterMethod
66debugPrint = pure . PM $ const return Nothing <=< liftIO . putStrLn . toString
67
68toString :: Printout -> String
69toString = undefined
70~~~