process the rest of Pieter's comments
[phd-thesis.git] / top / lang.tex
1 \documentclass[../thesis.tex]{subfiles}
2
3 \input{subfilepreamble}
4
5 \setcounter{chapter}{4}
6
7 \begin{document}
8 \input{subfileprefix}
9 \chapter{The mTask language}%
10 \label{chp:mtask_dsl}
11 \begin{chapterabstract}
12 This chapter introduces the \gls{TOP} language \gls{MTASK} by:
13 \begin{itemize}
14 \item introducing class-based shallow embedding and the setup of the \gls{MTASK} language;
15 \item describing briefly the various interpretations;
16 \item demonstrating how the type system is leveraged to enforce all constraints;
17 \item showing the language interface for expressions, datatypes, and functions;
18 \item and explaining the tasks, task combinators, and \glspl{SDS}.
19 \end{itemize}
20 \end{chapterabstract}
21
22 Regular \gls{FP} and \gls{TOP} languages do not run on resource-constrained edge devices.
23 A \gls{DSL} is therefore used as the basis of the \gls{MTASK} system, a complete \gls{TOP} programming environment for programming microcontrollers.
24 It is implemented as an \gls{EDSL} in \gls{CLEAN} using class-based---or tagless-final---embedding.
25 This means that the language interface, i.e.\ the language constructs, are a collection of type classes.
26 Interpretations of this interface are data types implementing these classes.
27 Due to the nature of this embedding technique, it is possible to have multiple interpretations for programs written in the \gls{MTASK} language.
28 Furthermore, this particular type of embedding has the property that it is extensible both in language constructs and in interpretations.
29 Adding a language construct is as simple as adding a type class.
30 Adding an interpretation is done by creating a new data type and providing implementations for the various type classes.
31
32 \todo[inline]{Is dit niet de plek om uit te leggen welke restricties we aan mTask opleggen om het op een edge device te kunnen draaien?
33 1 onderscheid tussen mTask en de rest van het programma
34 2 mTask uit kunnen voeren zonder heap gebruik anders dan de task expressie. Dus:
35 a geen recursieve data types
36 b geen hogere orde functies
37 c strict evaluation
38 d functies en objecten alleen op topniveau
39 Nu lijkt het af en toe dat mTask onnodig primitief is, terwijl we het ook algemener hadden kunnen doen.}
40
41 \section{Class-based shallow embedding}
42 Let us illustrate this technique by taking the very simple language of literal values.
43 This language interface can be described using a single type constructor class with a single function \cleaninline{lit}.
44 This function is for lifting values, when it has a \cleaninline{toString} instance, from the host language to our new \gls{DSL}.
45 The type variable \cleaninline{v} of the type class represents the view on the language, the interpretation.
46
47 \begin{lstClean}
48 class literals v where
49 lit :: a -> v a | toString a
50 \end{lstClean}
51
52 Providing an evaluator is straightforward as can be seen in the following listing.
53 The evaluator is just a box holding a value of the computation, but it can also be something more complex such as a monadic computation.
54
55 \begin{lstClean}
56 :: Eval a = Eval a
57
58 runEval :: (Eval a) -> a
59 runEval (Eval a) = a
60
61 instance literals Eval where
62 lit a = Eval a
63 \end{lstClean}
64
65 Extending the language with a printer is done by defining a new data type and providing instances for the type constructor classes.
66 The printer shown below only stores a printed representation and hence the type variable is just a phantom type:
67
68 \begin{lstClean}
69 :: Printer a = Printer String
70
71 runPrinter :: (Printer a) -> String
72 runPrinter (Printer a) = a
73
74 instance literals Printer where
75 lit a = Printer (toString a)
76 \end{lstClean}
77
78 Adding language constructs happens by defining new type classes and giving implementations for the interpretations.
79 The following listing adds an addition construct to the language and shows the implementations for the evaluator and printer.
80
81 \begin{lstClean}
82 class addition v where
83 add :: v a -> v a -> v a | + a
84
85 instance addition Eval where
86 add (Eval l) (Eval r) = Eval (l + r)
87
88 instance addition Printer where
89 add (Printer l) (Printer r) = Printer ("(" +++ l +++ "+" +++ r +++ ")")
90 \end{lstClean}
91
92 Terms in our toy language can be overloaded in their interpretation, they are just an interface.
93 For example, $1+5$ is written as \cleaninline{add (lit 1) (lit 5)} and has the type \cleaninline{v Int \| literals, addition v}.
94 However, due to the way let-polymorphism is implemented in most functional languages, it is not always straightforward to use multiple interpretations in one function.
95 Creating such a function, e.g.\ one that both prints and evaluates an expression, requires rank-2 polymorphism (see \cref{lst:rank2_mtask}).
96
97 \section{Types}
98 The \gls{MTASK} language is a tagless-final \gls{EDSL} as well.
99 As it is shallowly embedded, the types of the terms in the language can be constrained by type classes.
100 Types in the \gls{MTASK} language are expressed as types in the host language, to make the language type safe.
101 However, not all types in the host language are suitable for microcontrollers that may only have \qty{2}{\kibi\byte} of \gls{RAM}, so class constraints are added to the \gls{DSL} functions.
102 \Cref{tbl:mtask-c-datatypes} shows the mapping from \gls{CLEAN} types to \ccpp{} types.
103 The most used class constraint is the \cleaninline{type} class collection containing functions for serialisation, printing, \gls{ITASK} constraints, \etc\ \citep[\citesection{6.9}]{plasmeijer_clean_2021}.
104 Most of these functions are automatically derivable using generic programming but can be specialised when needed.
105 An even stronger restriction is defined for types that have a stack representation.
106 This \cleaninline{basicType} class has instances for many \gls{CLEAN} basic types such as \cleaninline{Int}, \cleaninline{Real} and \cleaninline{Bool}.
107 These class constraints for values in \gls{MTASK} are omnipresent in all functions and therefore usually omitted for brevity and clarity.
108
109 \begin{table}[ht]
110 \centering
111 \caption{Translation from \cmtask{} data types to \ccpp{} datatypes.}%
112 \label{tbl:mtask-c-datatypes}
113 \begin{tabular}{lll}
114 \toprule
115 \cmtask{} & \ccpp{} & \textnumero{}bits\\
116 \midrule
117 \cleaninline{Bool} & \cinline{bool} & 16\\
118 \cleaninline{Char} & \cinline{char} & 16\\
119 \cleaninline{Int} & \cinline{int16_t}\footnotemark{} & 16\\
120 \cleaninline{Real} & \cinline{float} & 32\\
121 \cleaninline{:: Long} & \cinline{int32_t} & 32\\
122 \cleaninline{:: T = A \| B \| C} & \cinline{enum} & 16\\
123 \bottomrule
124 \end{tabular}
125 \end{table}
126
127 \footnotetext{In \gls{ARDUINO} \ccpp{} this usually equals a \cinline{long}.}
128 \Cref{lst:constraints} contains the definitions for the auxiliary types and type constraints (such as \cleaninline{type} and \cleaninline{basicType}) that are used to construct \gls{MTASK} expressions.
129
130 \begin{lstClean}[caption={Classes and class collections for the \gls{MTASK} language.},label={lst:constraints}]
131 class type t | iTask, ... , fromByteCode, toByteCode t
132 class basicType t | type t where ...
133 \end{lstClean}
134
135 The \gls{MTASK} language interface consists of a core collection of type classes bundled in the type class \cleaninline{class mtask} (see \cref{lst:collection}).
136 Every interpretation of \gls{MTASK} terms implements the type classes in the \cleaninline{mtask} class collection.
137 %Optional \gls{MTASK} constructs such as peripherals or lowered \gls{ITASK} \glspl{SDS} are not included in this class collection because not all devices or interpretations support this.
138
139 \begin{lstClean}[caption={Class collection for the \gls{MTASK} language.},label={lst:collection}]
140 class mtask v | expr, ..., int, real, long v
141 \end{lstClean}
142
143 Peripheral, \gls{SDS}, and function definitions are always defined at the top level of \gls{MTASK} programs.
144 This is enforced by the \cleaninline{Main} type.
145 Most top level definitions are defined using \gls{HOAS}.
146 To make their syntax friendlier, the \cleaninline{In} type---an infix tuple---is used to combine these top level definitions.
147 To illustrate the structure of a \gls{MTASK} programs, \cref{lst:mtask_types} shows a skeleton of a program.
148
149 % VimTeX: SynIgnore on
150 \begin{lstClean}[caption={Auxiliary types and example task in the \gls{MTASK} language.},label={lst:mtask_types}]
151 // From the mTask library
152 :: Main a = { main :: a }
153 :: In a b = (In) infix 0 a b
154
155 someTask :: MTask v Int | mtask v & lowerSds v & sensor1 v & ...
156 someTask =
157 sensor1 config1 \sns1->
158 sensor2 config2 \sns2->
159 sds \s1 = initialValue
160 In lowerSds \s2 = someiTaskSDS
161 In fun \fun1= ( \(a0, a1)->... )
162 In fun \fun2= ( \a->... )
163 In { main = mainexpr }
164 \end{lstClean}
165 % VimTeX: SynIgnore off
166
167 Expressions in the \gls{MTASK} language are usually overloaded in their interpretation (\cleaninline{v}).
168 In \gls{CLEAN}, all free variables in a type are implicitly universally quantified.
169 In order to use the \gls{MTASK} expressions with multiple interpretations, rank-2 polymorphism is required \citep{odersky_putting_1996}.
170 \Cref{lst:rank2_mtask} shows an example of a function that simulates an \gls{MTASK} expression while showing the pretty printed representation in parallel.
171 Providing a type for the \cleaninline{simulateAndPrint} function is mandatory as the compiler cannot infer the type of rank-2 polymorphic functions\citep[\citesection{3.7.4}]{plasmeijer_clean_2021}.
172
173 \begin{lstClean}[label={lst:rank2_mtask},caption={Rank-2 polymorphism to allow multiple interpretations.}]
174 simulateAndPrint :: (A.v: Main (MTask v a) | mtask v) -> Task a | type a
175 simulateAndPrint mt =
176 simulate mt
177 -|| Hint "Current task:" @>> viewInformation [] (showMain mt)
178 \end{lstClean}
179
180 \section{Expressions}\label{sec:expressions}
181 This section shows all \gls{MTASK} language constructs for expressions.
182 \Cref{lst:expressions} shows the \cleaninline{expr} class containing the functionality to:
183 lift values from the host language to the \gls{MTASK} language (\cleaninline{lit});
184 perform numeric and boolean arithmetics;
185 do comparisons;
186 and perform conditional execution.
187 For every common boolean and arithmetic operator in the host language, an \gls{MTASK} variant is present.
188 The operators are suffixed by a period to not clash with the built-in operators in \gls{CLEAN}.
189
190 \begin{lstClean}[caption={The \gls{MTASK} class for expressions.},label={lst:expressions}]
191 class expr v where
192 lit :: t -> v t | type t
193 (+.) infixl 6 :: (v t) (v t) -> v t | basicType, +, zero t
194 ...
195 (&.) infixr 3 :: (v Bool) (v Bool) -> v Bool
196 (|.) infixr 2 :: (v Bool) (v Bool) -> v Bool
197 Not :: (v Bool) -> v Bool
198 (==.) infix 4 :: (v a) (v a) -> v Bool | Eq, basicType a
199 ...
200 If :: (v Bool) (v t) (v t) -> v t | type t
201 \end{lstClean}
202
203 Conversion to and fro data types is available through the overloaded functions \cleaninline{int}, \cleaninline{long} and \cleaninline{real}.
204 These functions convert the argument to the respective type similar to casting in \gls{C}.
205 For most interpretations, there are instances of these classes for all numeric types.
206
207 \begin{lstClean}[caption={Type conversion functions in \gls{MTASK}.}]
208 class int v a :: (v a) -> v Int
209 class real v a :: (v a) -> v Real
210 class long v a :: (v a) -> v Long
211 \end{lstClean}
212
213 Values from the host language must be explicitly lifted to the \gls{MTASK} language using the \cleaninline{lit} function.
214 For convenience, there are many lower-cased macro definitions for often-used constants such as \cleaninline{true :== lit True}, \cleaninline{false :== lit False}.
215
216 \Cref{lst:example_exprs} shows some examples of expressions in the \gls{MTASK} language.
217 Since they are only expressions, there is no need for a \cleaninline{Main}.
218 \cleaninline{e0} defines the literal \num{42}, \cleaninline{e1} calculates the literal \num{42.0} using real numbers and uses a type conversion.
219 \cleaninline{e2} compares \cleaninline{e0} and \cleaninline{e1} as integers and if they are equal it returns $\frac{\text{\cleaninline{e2}}}{2}$ and \cleaninline{e0} otherwise.
220
221 \begin{lstClean}[label={lst:example_exprs},caption={Example \gls{MTASK} expressions.}]
222 e0 :: v Int | expr v
223 e0 = lit 42
224
225 e1 :: v Real | expr v
226 e1 = lit 38.0 +. real (lit 4)
227
228 e2 :: v Int | expr v
229 e2 = If (e0 ==. int e1)
230 (int e1 /. lit 2) e0
231 \end{lstClean}
232
233 The \gls{MTASK} language is shallowly embedded in \gls{CLEAN} and the terms are constructed and hence compiled at run time.
234 This means that \gls{MTASK} programs can also be tailor-made at run time using \gls{CLEAN} functions, maximising the linguistic reuse \citep{krishnamurthi_linguistic_2001}.
235 The \cleaninline{approxEqual} function in \cref{lst:example_macro} is an example of this.
236 It performs a simple approximate equality---admittedly not taking into account all floating point peculiarities.
237 When calling \cleaninline{approxEqual} in an \gls{MTASK} expression, the resulting code is inlined.
238
239 \begin{lstClean}[label={lst:example_macro},caption={Approximate equality as an example of linguistic reuse in \gls{MTASK}.}]
240 approxEqual :: (v Real) (v Real) (v Real) -> v Bool | expr v
241 approxEqual x y eps = x ==. y |. If (x >. y)
242 (x -. y <. eps)
243 (y -. x <. eps)
244 \end{lstClean}
245
246 \subsection{Data types}
247 Most of the fixed-size basic types from \gls{CLEAN} are mapped on \gls{MTASK} types (see \cref{tbl:mtask-c-datatypes}).
248 However, it is useful to have access to compound types as well.
249 All types in \gls{MTASK} have a fixed-size representation on the stack, so sum types are not (yet) supported.
250 It is possible to lift any types, e.g.\ tuples, using the \cleaninline{lit} function as long as they have instances for the required type classes.
251 However, you cannot do anything with values of the types besides passing them around.
252 To be able to use types as first-class citizens, constructors, and field selectors or deconstructors are required (see \cref{chp:first-class_datatypes}).
253 \Cref{lst:tuple_exprs} shows the scaffolding required for supporting tuples in \gls{MTASK}.
254 Besides the constructors and field selectors, there is also a helper function available that transforms a function from a tuple of \gls{MTASK} expressions to an \gls{MTASK} expression of a tuple, a deconstructor.
255 Examples of \gls{MTASK} programs using tuples are seen later in \cref{sec:mtask_functions}.
256
257 \begin{lstClean}[label={lst:tuple_exprs},caption={Tuple constructor and field selectors in \gls{MTASK}.}]
258 class tupl v where
259 tupl :: (v a) (v b) -> v (a, b) | type a & type b
260 first :: (v (a, b)) -> v a | type a & type b
261 second :: (v (a, b)) -> v b | type a & type b
262
263 tupopen :: ((v a, v b) -> v c) -> ((v (a, b)) -> v c)
264 tupopen f :== \v->f (first v, second v)
265 \end{lstClean}
266
267 \subsection{Functions}\label{sec:mtask_functions}
268 Adding functions to the language is achieved by one type class to the \gls{DSL}.
269 By using \gls{HOAS}, both the function definition and the calls to the function can be controlled by the \gls{DSL} \citep{pfenning_higher-order_1988,chlipala_parametric_2008}.
270 The \gls{MTASK} language only allows first-order functions and no partial function application.
271 To restrict this, a multi-parameter type class is used instead of a type class with one type variable.
272 The first parameter represents the shape of the arguments, the second parameter the interpretation.
273 An instance is provided for each function arity instead of providing an instance for all possible arities to enforce that all functions are first order.
274 By using argument tuples to represent the arity of the function, partial function applications are eradicated.
275 The definition of the type class and some instances for the pretty printer are as follows:
276
277 \begin{lstClean}[caption={Functions in \gls{MTASK}.}]
278 class fun a v :: ((a -> v s) -> In (a -> v s) (Main (MTask v u)))
279 -> Main (MTask v u)
280
281 instance fun () Show where ...
282 instance fun (Show a) Show | type a where ...
283 instance fun (Show a, Show b) Show | type a, type b where ...
284 instance fun (Show a, Show b, Show c) Show | type a, ... where ...
285 ...
286 \end{lstClean}
287
288 Deriving how to define and use functions from the type is quite a challenge even though the resulting syntax is made easier using the infix type \cleaninline{In}.
289 \Cref{lst:function_examples} show some examples of functions to illustrate the syntax.
290 Splitting out the function definition for each single arity means that for every function arity and combination of arguments, a separate class constraint must be created.
291 Many of the often used functions are already bundled in the \cleaninline{mtask} class constraint collection.
292 The \cleaninline{factorial} functions shows a recursive version of the factorial function.
293 The \cleaninline{factorialtail} function is a tail-call optimised version of the factorial function.
294 It contains a manually added class constraint.
295 \todo[inline]{Uitleggen waarom je dit doet}
296 Zero-arity functions are always called with unit as an argument.
297 An illustration of this is seen in the \cleaninline{zeroarity} expression.
298 Finally, \cleaninline{swapTuple} shows an example of a tuple being swapped.
299
300 % VimTeX: SynIgnore on
301 \begin{lstClean}[label={lst:function_examples},caption={Examples of various functions in \gls{MTASK}.}]
302 factorial :: Main (v Int) | mtask v
303 factorial =
304 fun \fac=(\i->If (i <. lit 1)
305 (lit 1)
306 (i *. fac (i -. lit 1)))
307 In {main = fac (lit 5) }
308
309 factorialtail :: Main (v Int) | mtask v & fun (v Int, v Int) v
310 factorialtail =
311 fun \facacc=(\(acc, i)->If (i <. lit 1)
312 acc
313 (fac (acc *. i, i -. lit 1)))
314 In fun \fac=(\i->facacc (lit 1, i))
315 In {main = fac (lit 5) }
316
317 zeroarity :: Main (v Int) | mtask v
318 zeroarity =
319 fun \fourtytwo=(\()->lit 42)
320 In fun \add=(\(x, y)->x +. y)
321 In {main = add (fourtytwo (), lit 9)}
322
323 swapTuple :: Main (v (Int, Bool)) | mtask v
324 swapTuple =
325 fun \swap=(tupopen \(x, y)->tupl y x)
326 In {main = swap (tupl true (lit 42)) }
327 \end{lstClean}
328 % VimTeX: SynIgnore off
329
330 \section{Tasks and task combinators}\label{sec:top}
331 This section describes the task language of \gls{MTASK}.
332 \Gls{TOP} languages are programming languages enriched with tasks.
333 Tasks represent abstract pieces of work and can be combined using combinators.
334 Creating tasks is done by evaluating expressions.
335 The result of an evaluated task expression is called a task tree, a run time representation of a task.
336 In order to evaluate a task, the resulting task tree is \emph{rewritten}, i.e.\ similar to rewrite systems, they perform a bit of work, step by step.
337 With each step, a task value is yielded that is observable by other tasks and can be acted upon.
338
339 The implementation in the \gls{MTASK} \gls{RTS} for task execution is shown in \cref{chp:implementation}.
340 The following sections show the definitions of the functions for creating tasks.
341 They also show the semantics of tasks: their observable value in relation to the work that the task represents.
342 The task language of \gls{MTASK} is divided into three categories:
343
344 \begin{description}
345 \item [Basic tasks] are the leaves in the task trees.
346 In most \gls{TOP} systems, the basic tasks are called editors, modelling the interactivity with the user.
347 In \gls{MTASK}, there are no editors in that sense.
348 Editors in \gls{MTASK} model the interaction with the outside world through peripherals such as sensors and actuators.
349 \item [Task combinators] provide a way of describing the workflow or collaboration.
350 They combine one or more tasks into a compound task.
351 \item [\Glspl{SDS}] are typed references to shared memory in \gls{MTASK}.
352 The data is available for tasks using many-to-many communication but only from within the task language to ensure atomicity.
353 \end{description}
354
355 As \gls{MTASK} is integrated with \gls{ITASK}, a stability distinction is made for task values just as in \gls{ITASK}.
356 A task in \gls{MTASK} is denoted by the \gls{DSL} type synonym shown in \cref{lst:task_type}.
357 A task is an expression of the type \cleaninline{TaskValue a} in interpretation \cleaninline{v}.
358
359 \begin{lstClean}[label={lst:task_type},caption={Task type in \gls{MTASK}.}]
360 :: MTask v a :== v (TaskValue a)
361
362 // From the iTask library
363 :: TaskValue a
364 = NoValue
365 | Value a Bool
366 \end{lstClean}
367
368 \subsection{Basic tasks}
369 The \gls{MTASK} language contains interactive and non-interactive basic tasks.
370 As \gls{MTASK} is integrated in \gls{ITASK}, the same notion of stability is applied to the observable task value (see \cref{fig:taskvalue}).
371 \todo[inline]{Stability beter uitleggen?}
372 The most rudimentary non-interactive basic tasks in the task language of \gls{MTASK} are \cleaninline{rtrn} and \cleaninline{unstable}.
373 They lift the value from the \gls{MTASK} expression language to the task domain either as a stable or unstable value.
374 There is also a special type of basic task for delaying execution.
375 The \cleaninline{delay} task---parametrised by a number of milliseconds---yields an unstable value while the time has not passed.
376 Once the specified time has passed, the time it overshot the planned time is yielded as a stable task value.
377 See \cref{sec:repeat} for an example task using \cleaninline{delay}.
378
379 \begin{lstClean}[label={lst:basic_tasks},caption={Non-interactive basic tasks in \gls{MTASK}.}]
380 class rtrn v :: (v t) -> MTask v t
381 class unstable v :: (v t) -> MTask v t
382 class delay v :: (v n) -> MTask v n | long v n
383 \end{lstClean}
384
385 \subsubsection{Peripherals}\label{sssec:peripherals}
386 In order for the edge device to interact with the environment, peripherals such as sensors and actuators are employed.
387 Some peripherals are available on the microcontroller package, others are connected with wires using protocols such as \gls{I2C}.
388 For every supported sensor or actuator, basic tasks are available that allow interaction with the specific peripheral.
389 The type classes for these tasks are not included in the \cleaninline{mtask} class collection as not all devices nor all language interpretations support every peripheral connected.
390
391 An example of a built-in peripheral is the \gls{GPIO} system.
392 This array of digital and analogue pins is controlled through software.
393 \Gls{GPIO} access is divided into three classes: analogue \gls{IO}, digital \gls{IO} and pin mode configuration (see \cref{lst:gpio}).
394 For all pins and pin modes, an \gls{ADT} is available that enumerates the pins.
395 The analogue \gls{GPIO} pins of a microcontroller are connected to an \gls{ADC} that translates the measured voltage to an integer.
396 Digital \gls{GPIO} pins of a microcontroller report either a high or a low value.
397 Both analogue and digital \gls{GPIO} pins can be read or written to, but it is advised to set the pin mode accordingly.
398 The \cleaninline{pin} type class allows functions to be overloaded in either the analogue or digital pins, e.g.\ analogue pins can be considered digital pins as well.
399
400 For digital \gls{GPIO} interaction, the \cleaninline{dio} type class is used.
401 The first argument of the functions in this class is overloaded, i.e.\ it accepts either an \cleaninline{APin} or a \cleaninline{DPin}.
402 Analogue \gls{GPIO} tasks are bundled in the \cleaninline{aio} type class.
403 \Gls{GPIO} pins usually operate according to a certain pin mode that states whether the pin acts as an input pin, an input with an internal pull-up resistor or an output pin.
404 This setting can be set using the \cleaninline{pinMode} class by hand or by using the \cleaninline{declarePin} \gls{GPIO} pin constructor to declare it at the top level.
405 Setting the pin mode is a task that immediately finishes and yields a stable unit value.
406 Writing to a pin is also a task that immediately finishes, but yields the written value.
407 Reading a pin is a task that yields an unstable value---i.e.\ the value read from the actual pin.
408
409 \begin{lstClean}[label={lst:gpio},caption={The \gls{MTASK} interface for \gls{GPIO} access.}]
410 :: APin = A0 | A1 | A2 | A3 | A4 | A5
411 :: DPin = D0 | D1 | D2 | D3 | D4 | D5 | D6 | D7 | D8 | D9 | D10 | ...
412 :: PinMode = PMInput | PMOutput | PMInputPullup
413 :: Pin = AnalogPin APin | DigitalPin DPin
414
415 class pin p :: p -> Pin
416 instance pin APin, DPin
417
418 class aio v where
419 writeA :: (v APin) (v Int) -> MTask v Int
420 readA :: (v APin) -> MTask v Int
421
422 class dio p v | pin p where
423 writeD :: (v p) (v Bool) -> MTask v Bool
424 readD :: (v p) -> MTask v Bool | pin p
425
426 class pinMode v where
427 pinMode :: (v PinMode) (v p) -> MTask v () | pin p
428 declarePin :: p PinMode ((v p) -> Main (v a)) -> Main (v a) | pin p
429 \end{lstClean}
430
431 \Cref{lst:gpio_examples} shows two examples of \gls{MTASK} tasks using \gls{GPIO} pins.
432 \cleaninline{task1} reads analogue \gls{GPIO} pin 3.
433 This is a task that never terminates.
434 \cleaninline{task2} writes the \cleaninline{true} (\gls{ARDUINO} \arduinoinline{HIGH}) value to digital \gls{GPIO} pin 3.
435 This task finishes immediately after writing to the pin.
436
437 \begin{lstClean}[label={lst:gpio_examples},caption={\Gls{GPIO} example in \gls{MTASK}.}]
438 task1 :: MTask v Int | mtask v
439 task1 = declarePin A3 PMInput \a3->{main=readA a3}
440
441 task2 :: MTask v Int | mtask v
442 task2 = declarePin D3 PMOutput \d3->{main=writeD d3 true}
443 \end{lstClean}
444
445 Peripherals are bundled by their functionality in \gls{MTASK}.
446 For example, \cref{lst:dht} shows the type classes for all supported \gls{DHT} sensors.
447 Currently, two different types of \gls{DHT} sensors are supported, the \emph{DHT} family of sensors connect through the \gls{ONEWIRE} protocol and the \emph{SHT} family of sensors connected using the \gls{I2C} protocol.
448 Creating such a \cleaninline{DHT} object is very similar to creating functions in \gls{MTASK} and uses \gls{HOAS} to make it type safe.
449 When provided a configuration and a configuration function, the \gls{DHT} object can be brought into scope.
450 For the \gls{DHT} sensor there are two basic tasks, \cleaninline{temperature} and \cleaninline{humidity}, that produce a task that yields the observed temperature in \unit{\celcius} or the relative humidity as an unstable value.
451 Other peripherals have similar interfaces, they are available in \cref{sec:aux_peripherals}.
452
453 \begin{lstClean}[label={lst:dht},caption={The \gls{MTASK} interface for \glspl{DHT} sensors.}]
454 :: DHT //abstract
455 :: DHTInfo
456 = DHT_DHT Pin DHTtype
457 | DHT_SHT I2CAddr
458 :: DHTtype = DHT11 | DHT21 | DHT22
459 class dht v where
460 DHT :: DHTInfo ((v DHT) -> Main (v b)) -> Main (v b) | type b
461 temperature :: (v DHT) -> MTask v Real
462 humidity :: (v DHT) -> MTask v Real
463
464 measureTemp :: Main (MTask v Real) | mtask, dht v
465 measureTemp = DHT (DHT_SHT (i2c 0x36)) \dht->{main=temperature dht}
466 \end{lstClean}
467
468 \subsection{Task combinators}
469 Task combinators are used to combine multiple tasks to describe workflows.
470 The \gls{MTASK} language has a set of simpler combinators from which more complicated workflow can be derived.
471 There are three main types of task combinators, namely:
472 \begin{itemize}
473 \item sequential combinators that execute tasks one after the other, possibly using the result of the left-hand side;
474 \item parallel combinators that execute tasks at the same time, combining the result;
475 \item miscellaneous combinators that change the semantics of a task---for example a combinator that repeats the child task.
476 \end{itemize}
477
478 \subsubsection{Sequential}
479 Sequential task combination allows the right-hand side expression to \emph{observe} the left-hand side task value.
480 All sequential task combinators are defined in the \cleaninline{step} class and are by default defined in terms of the Swiss Army knife step combinator (\cleaninline{>>*.}, \cref{lst:mtask_sequential}).
481 This combinator has a single task on the left-hand side and a list of \emph{task continuations} on the right-hand side.
482 Every rewrite step, the list of task continuations are tested on the task value.
483 If one of the predicates matches, the task continues with the result of these continuations.
484 Several shorthand combinators are derived from the step combinator.
485 \cleaninline{>>=.} is a shorthand for the bind operation, if the left-hand side is stable, the right-hand side function is called to produce a new task.
486 \cleaninline{>>\|.} is a shorthand for the sequence operation, if the left-hand side is stable, it continues with the right-hand side task.
487 The \cleaninline{>>~.} and \cleaninline{>>..} combinators are variants of the ones above that ignore the stability and continue on an unstable value as well.
488
489 \begin{lstClean}[label={lst:mtask_sequential},caption={Sequential task combinators in \gls{MTASK}.}]
490 class step v | expr v where
491 (>>*.) infixl 1 :: (MTask v t) [Step v t u] -> MTask v u
492 (>>=.) infixl 0 :: (MTask v t) ((v t) -> MTask v u) -> MTask v u
493 (>>|.) infixl 0 :: (MTask v t) (MTask v u) -> MTask v u
494 (>>~.) infixl 0 :: (MTask v t) ((v t) -> MTask v u) -> MTask v u
495 (>>..) infixl 0 :: (MTask v t) (MTask v u) -> MTask v u
496
497 :: Step v t u
498 = IfValue ((v t) -> v Bool) ((v t) -> MTask v u)
499 | IfStable ((v t) -> v Bool) ((v t) -> MTask v u)
500 | IfUnstable ((v t) -> v Bool) ((v t) -> MTask v u)
501 | Always (MTask v u)
502 \end{lstClean}
503
504 The following listing shows an example of a step in action.
505 The \cleaninline{readPinBin} function produces an \gls{MTASK} task that classifies the value of an analogue pin into four bins.
506 It also shows that the nature of embedding allows the host language to be used as a macro language.
507
508 \begin{lstClean}[label={lst:mtask_readpinbin},caption={Read an analogue pin and bin the value in \gls{MTASK}.}]
509 readPinBin :: Int -> Main (MTask v Int) | mtask v
510 readPinBin lim = declarePin PMInput A2 \a2->
511 { main = readA a2 >>*.
512 [ IfValue (\x->x <. lim) (\_->rtrn (lit bin))
513 \\ lim <- [64,128,192,256]
514 & bin <- [0..]]}
515 \end{lstClean}
516
517 \subsubsection{Parallel}\label{sssec:combinators_parallel}
518 The result of a parallel task combination is a compound task that executes both tasks at the same time.
519 There are two types of parallel task combinators in the \gls{MTASK} language (see \cref{lst:mtask_parallel}).
520
521 \begin{lstClean}[label={lst:mtask_parallel},caption={Parallel task combinators in \gls{MTASK}.}]
522 class (.&&.) infixr 4 v :: (MTask v a) (MTask v b) -> MTask v (a, b)
523 class (.||.) infixr 3 v :: (MTask v a) (MTask v a) -> MTask v a
524 \end{lstClean}
525
526 The conjunction combinator (\cleaninline{.&&.}) combines the result by putting the values from both sides in a tuple.
527 The stability of the task depends on the stability of both children.
528 If both children are stable, the result is stable, otherwise the result is unstable.
529 The disjunction combinator (\cleaninline{.\|\|.}) combines the results by picking the leftmost, most stable task.
530 The semantics of both parallel combinators are most easily described using the \gls{CLEAN} functions shown in \cref{lst:semantics_con,lst:semantics_dis}.
531
532 \begin{figure}[ht]
533 \centering
534 \begin{subfigure}[t]{.5\textwidth}
535 \begin{lstClean}[caption={Semantics of the\\conjunction combinator.},label={lst:semantics_con}]
536 con :: (TaskValue a) (TaskValue b)
537 -> TaskValue (a, b)
538 con NoValue r = NoValue
539 con l NoValue = NoValue
540 con (Value l ls) (Value r rs)
541 = Value (l, r) (ls && rs)
542
543 \end{lstClean}
544 \end{subfigure}%
545 \begin{subfigure}[t]{.5\textwidth}
546 \begin{lstClean}[caption={Semantics of the\\disjunction combinator.},label={lst:semantics_dis}]
547 dis :: (TaskValue a) (TaskValue a)
548 -> TaskValue a
549 dis NoValue r = r
550 dis l NoValue = l
551 dis (Value l ls) (Value r rs)
552 | rs && not ls = Value r rs
553 | otherwise = Value l ls
554 \end{lstClean}
555 \end{subfigure}
556 \end{figure}
557
558 \Cref{lst:mtask_parallel_example} gives an example program that uses the parallel task combinator.
559 This task read two pins at the same time, returning when one of the pins becomes high.
560 If the combinator was the \cleaninline{.&&.}, the type would be \cleaninline{MTask v (Bool, Bool)} and the task would only return when both pins are high but not necessarily at the same time.
561
562 \begin{lstClean}[label={lst:mtask_parallel_example},caption={Parallel task combinator example in \gls{MTASK}.}]
563 task :: MTask v Bool
564 task =
565 declarePin D0 PMInput \d0->
566 declarePin D1 PMInput \d1->
567 let monitor pin = readD pin >>*. [IfValue id rtrn]
568 In {main = monitor d0 .||. monitor d1}
569 \end{lstClean}
570
571 \subsubsection{Repeat}\label{sec:repeat}
572 In many workflows, tasks are to be executed repeatedly.
573 The \cleaninline{rpeat} combinator does this by executing the child task until it becomes stable.
574 Once a stable value is observed, the task is reinstated.
575 The functionality of \cleaninline{rpeat} can also be simulated by using functions and sequential task combinators and even made to be stateful as can be seen from the blink example from \cref{chp:top4iot}.
576
577 \begin{lstClean}[label={lst:mtask_repeat},caption={Repeat task combinators in \gls{MTASK}.}]
578 class rpeat v where
579 rpeat :: (MTask v a) -> MTask v a
580 \end{lstClean}
581
582 \Cref{lst:mtask_repeat_example} shows an example of a task that uses the \cleaninline{rpeat} combinator.
583 This resulting task mirrors the value read from analogue \gls{GPIO} pin \cleaninline{A1} to pin \cleaninline{A2} by constantly reading the pin and writing the result.
584
585 \begin{lstClean}[label={lst:mtask_repeat_example},caption={Repeat task combinator example in \gls{MTASK}.}]
586 task :: MTask v Int | mtask v
587 task = declarePin A1 PMInput \a1->
588 declarePin A2 PMOutput \a2->
589 {main = rpeat (readA a1 >>~. writeA a2 >>|. delay (lit 1000))}
590 \end{lstClean}
591
592 \subsection{Shared data sources}
593 For some collaborations it is cumbersome to only communicate data using task values.
594 \Glspl{SDS} are a safe abstraction over any data that fill this gap.
595 It allows tasks to safely and atomically read, write and update data stores in order to share data with other tasks.
596 \Glspl{SDS} in \gls{MTASK} are by default references to shared memory but can also be references to \glspl{SDS} in \gls{ITASK} (see \cref{sec:liftsds}).
597 There are no combinators or user-defined \gls{SDS} types in \gls{MTASK} as there are in \gls{ITASK}.
598 Similar to peripherals and functions, \glspl{SDS} are defined at the top level with the \cleaninline{sds} function.
599 They are accessed through interaction tasks.
600 The \cleaninline{getSds} task yields the current value of the \gls{SDS} as an unstable value.
601 This behaviour is similar to the \cleaninline{watch} task in \gls{ITASK}.
602 Writing a new value to \pgls{SDS} is done using \cleaninline{setSds}.
603 This task yields the written value as a stable result after it is done writing.
604 Getting and immediately setting \pgls{SDS} is not necessarily an \emph{atomic} operation in \gls{MTASK} because it is possible that another task accesses the \gls{SDS} in between.
605 To circumvent this issue, \cleaninline{updSds} is available by which \pgls{SDS} can be atomically updated.
606 The \cleaninline{updSds} task only guarantees atomicity within \gls{MTASK}.
607
608 \begin{lstClean}[label={lst:mtask_sds},caption={\Glspl{SDS} in \gls{MTASK}.}]
609 :: Sds a // abstract
610 class sds v where
611 sds :: ((v (Sds t)) -> In t (Main (MTask v u))) -> Main (MTask v u)
612 getSds :: (v (Sds t)) -> MTask v t
613 setSds :: (v (Sds t)) (v t) -> MTask v t
614 updSds :: (v (Sds t)) ((v t) -> v t) -> MTask v t
615 \end{lstClean}
616
617 \Cref{lst:mtask_sds_examples} shows an example task that uses \glspl{SDS}.
618 The \cleaninline{count} function takes a pin and returns a task that counts the number of times the pin is observed as high by incrementing the \cleaninline{share} \gls{SDS}.
619 In the \cleaninline{main} expression, this function is called twice with a different argument.
620 The results are combined using the parallel disjunction combinator (\cleaninline{.\|\|.}).
621 Using a sequence of \cleaninline{getSds} and \cleaninline{setSds} would be unsafe here because the other branch might write their old increment value immediately after writing, effectively missing a count.
622
623 \begin{lstClean}[label={lst:mtask_sds_examples},caption={Examples with \glspl{SDS} in \gls{MTASK}.}]
624 task :: MTask v Int | mtask v
625 task =
626 declarePin D3 PMInput \d3->
627 declarePin D5 PMInput \d5->
628 sds \share=0
629 In fun \count=(\pin->
630 readD pin
631 >>* [IfValue id (\_->updSds (\x->x +. lit 1) share)]
632 >>| delay (lit 100) // debounce
633 >>| count pin)
634 In {main=count d3 .||. count d5}
635 \end{lstClean}
636
637 \section{Interpretations}
638 \todo[inline]{Iets meer uitleg over waarom dit zo handig is}
639 This section describes all the interpretations that the \gls{MTASK} language has to offer.
640 Not all these interpretations are necessarily \gls{TOP} engines, i.e.\ not all the interpretations execute the resulting tasks.
641 Some may perform an analysis over the program or typeset the program.
642
643 \subsection{Pretty printer}
644 The pretty printer interpretation converts an \gls{MTASK} term to a string representation.
645 As the host language \gls{CLEAN} constructs the \gls{MTASK} expressions at run time, it can be useful to show the constructed expression at run time as well.
646 The only function exposed for this interpretation is the \cleaninline{showMain} function (\cref{lst:showmain}).
647 It runs the pretty printer and returns a list of strings containing the pretty printed result.
648 The pretty printing function does the best it can but obviously cannot reproduce layout, curried functions, and variable names.
649 This shortcoming is illustrated by printing a blink task that contains a function and currying in \cref{lst:showexample}.
650
651 \begin{lstClean}[caption={The entry point for the pretty printing interpretation.},label={lst:showmain}]
652 :: Show a // from the mTask pretty printing library
653 showMain :: (Main (Show a)) -> [String]
654 \end{lstClean}
655
656 \begin{lstClean}[caption={Pretty printing interpretation example.},label={lst:showexample}]
657 blinkTask :: Main (MTask v Bool) | mtask v
658 blinkTask =
659 fun \blink=(\state->
660 writeD d13 state >>|. delay (lit 500) >>=. blink o Not
661 ) In {main = blink true}
662
663 // output when printing:
664 // fun f0 a1 = writeD(D13, a1) >>= \a2.(delay 1000)
665 // >>| (f0 (Not a1)) in (f0 True)
666 \end{lstClean}
667
668 \subsection{Simulator}
669 The simulator converts the expression to a ready-for-work \gls{ITASK} simulation.
670 There is one entry point for this interpretation (see \cref{lst:simulatemain}).
671 The task resulting from the \cleaninline{simulate} function presents the user with an interactive simulation environment (see \cref{fig:sim}).
672 The simulation allows the user to (partially) execute tasks, control the simulated peripherals, inspect the internal state of the tasks, and interact with \glspl{SDS}.
673
674 \begin{lstClean}[caption={The entry point for the simulation interpretation.},label={lst:simulatemain}]
675 :: TraceTask a // from the mTask simulator library
676 simulate :: (Main (TraceTask a)) -> [String]
677 \end{lstClean}
678
679 \begin{figure}
680 \centering
681 \includegraphics[width=\linewidth]{simg}
682 \caption{Simulator interface for a blink program written in \gls{MTASK}.}\label{fig:sim}
683 \end{figure}
684
685 \subsection{Byte code compiler}
686 The main interpretation of the \gls{MTASK} system is the byte code compiler (\cleaninline{:: BCInterpret a}).
687 This interpretation compiles the \gls{MTASK} term at run time to byte code.
688 With it, and a handful of integration functions, \gls{MTASK} tasks can be executed on microcontrollers and integrated in \gls{ITASK} as if they were regular \gls{ITASK} tasks.
689 Furthermore, with a special language construct, \glspl{SDS} can be shared between \gls{MTASK} and \gls{ITASK} programs as well.
690 The integration with \gls{ITASK} is explained thoroughly later in \cref{chp:integration_with_itask}.
691
692 The \gls{MTASK} language together with \gls{ITASK} is a heterogeneous \gls{DSL}.
693 I.e.\ some components---for example the \gls{RTS} on the microcontroller that executes the tasks---is largely unaware of the other components in the system, and it is executed on a completely different architecture.
694 The \gls{MTASK} language is a \gls{TOP} language with basic tasks tailored for \gls{IOT} edge devices (see \cref{sec:top}).
695 It uses expressions based a simply-typed $\lambda$-calculus with support for some basic types, arithmetic operations, and function definitions.
696
697 \section{Conclusion}
698 This chapter gave an overview of the complete \gls{MTASK} \gls{DSL}.
699 The \gls{MTASK} language is a rich \gls{TOP} language tailored for \gls{IOT} edge devices.
700 The language is implemented as a class-based shallowly \gls{EDSL} in the pure functional host language \gls{CLEAN}.
701 The language is an enriched lambda calculus as a host language.
702 It provides language constructs for arithmetic expressions, conditionals, functions, but also non-interactive basic tasks, task combinators, peripheral support, and integration with \gls{ITASK}.
703 Terms in the language are just interfaces and can be interpreted by one or more interpretations.
704 When using the byte code compiler, terms in the \gls{MTASK} language are type checked at compile time but are constructed and compiled at run time.
705 This facilitates tailor-making tasks for the current work requirements.
706
707 \input{subfilepostamble}
708 \end{document}