X-Git-Url: https://git.martlubbers.net/?a=blobdiff_plain;f=top%2Flang.tex;h=a248aa52bd1b8da65035d92706b97a96355ebda9;hb=20d972f3af3f073519e6e3e23edf4eb725f8f52d;hp=a5916cdebd0ff393199b22ce9e290e4957272d52;hpb=3369704052f2381076b72efb24d57a943172e729;p=phd-thesis.git diff --git a/top/lang.tex b/top/lang.tex index a5916cd..a248aa5 100644 --- a/top/lang.tex +++ b/top/lang.tex @@ -2,125 +2,192 @@ \input{subfilepreamble} +\setcounter{chapter}{4} + \begin{document} \input{subfileprefix} - -\chapter{The \texorpdfstring{\gls{MTASK}}{mTask} language}%\texorpdfstring{\glsxtrshort{DSL}}{DSL}}% +\chapter{The mTask language}% \label{chp:mtask_dsl} \begin{chapterabstract} - \noindent This chapter introduces the \gls{TOP} language \gls{MTASK} language by: + This chapter introduces the \gls{TOP} language \gls{MTASK} by: \begin{itemize} - \item introducing the setup of the \gls{EDSL}; - \item and showing the language interface and examples for the type system, data types, expressions, tasks and their combinators. + \item introducing class-based shallow embedding and the setup of the \gls{MTASK} language; + \item describing briefly the various interpretations; + \item demonstrating how the type system is leveraged to enforce all constraints; + \item showing the language interface for expressions, datatypes, and functions; + \item and explaining the tasks, task combinators, and \glspl{SDS}. \end{itemize} \end{chapterabstract} -The \gls{MTASK} system is a complete \gls{TOP} programming environment for programming microcontrollers. -This means that it not only contains a \gls{TOP} language but also a \gls{TOP} engine. -It is implemented as an \gls{EDSL} in \gls{CLEAN} using class-based---or tagless-final---embedding (see \cref{sec:tagless-final_embedding}). -Due to the nature of the embedding technique, it is possible to have multiple interpretations for programs written in the \gls{MTASK} language. -Not all of these intepretations are necessarily \gls{TOP} engines, some may perform an analysis over the program or typeset the program so that the output can be shown after evaluating the expression at run time. -The following interpretations are available for \gls{MTASK}. +Regular \gls{FP} and \gls{TOP} languages do not run on resource-constrained edge devices. +A \gls{DSL} is therefore used as the basis of the \gls{MTASK} system, a complete \gls{TOP} programming environment for programming microcontrollers. +It is implemented as an \gls{EDSL} in \gls{CLEAN} using class-based---or tagless-final---embedding. +This means that the language interface, i.e.\ the language constructs, are a collection of type classes. +Interpretations of this interface are data types implementing these classes. +Due to the nature of this embedding technique, it is possible to have multiple interpretations for programs written in the \gls{MTASK} language. +Furthermore, this particular type of embedding has the property that it is extensible both in language constructs and in interpretations. +Adding a language construct is as simple as adding a type class. +Adding an interpretation is done by creating a new data type and providing implementations for the various type classes. + +\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? + 1 onderscheid tussen mTask en de rest van het programma + 2 mTask uit kunnen voeren zonder heap gebruik anders dan de task expressie. Dus: + a geen recursieve data types + b geen hogere orde functies + c strict evaluation + d functies en objecten alleen op topniveau +Nu lijkt het af en toe dat mTask onnodig primitief is, terwijl we het ook algemener hadden kunnen doen.} + +\section{Class-based shallow embedding} +Let us illustrate this technique by taking the very simple language of literal values. +This language interface can be described using a single type constructor class with a single function \cleaninline{lit}. +This function is for lifting values, when it has a \cleaninline{toString} instance, from the host language to our new \gls{DSL}. +The type variable \cleaninline{v} of the type class represents the view on the language, the interpretation. + +\begin{lstClean} +class literals v where + lit :: a -> v a | toString a +\end{lstClean} -\begin{description} - \item[Printer] +Providing an evaluator is straightforward as can be seen in the following listing. +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. - This interpretation converts the expression to a string representation. - As the host language \gls{CLEAN} constructs the \gls{MTASK} expressions at run time, it can be useful to show the constructed expression. - \item[Simulator] +\begin{lstClean} +:: Eval a = Eval a - The simulator converts the expression to a ready-for-work \gls{ITASK} simulation in which the user can inspect and control the simulated peripherals and see the internal state of the tasks. - \item[Byte code compiler] +runEval :: (Eval a) -> a +runEval (Eval a) = a - The compiler compiles the \gls{MTASK} program to a specialised byte code. - Using a handful of integration functions and tasks (see \cref{chp:integration_with_itask}), \gls{MTASK} tasks can be executed on microcontrollers and integrated in \gls{ITASK} as if they were regular \gls{ITASK} tasks. - Furthermore, with special language constructs, \glspl{SDS} can be shared between \gls{MTASK} and \gls{ITASK} programs. -\end{description} +instance literals Eval where + lit a = Eval a +\end{lstClean} + +Extending the language with a printer is done by defining a new data type and providing instances for the type constructor classes. +The printer shown below only stores a printed representation and hence the type variable is just a phantom type: + +\begin{lstClean} +:: Printer a = Printer String + +runPrinter :: (Printer a) -> String +runPrinter (Printer a) = a -When using the byte code compiler interpretation in conjunction with the \gls{ITASK} integration, \gls{MTASK} is a heterogeneous \gls{DSL}. -I.e.\ some components---e.g.\ the \gls{RTS} on the microcontroller---is largely unaware of the other components in the system, and it is executed on a completely different architecture. -The \gls{MTASK} language is an enriched simply-typed $\lambda$-calculus with support for some basic types, arithmetic operations, and function definition; and a task language (see \cref{sec:top}). +instance literals Printer where + lit a = Printer (toString a) +\end{lstClean} + +Adding language constructs happens by defining new type classes and giving implementations for the interpretations. +The following listing adds an addition construct to the language and shows the implementations for the evaluator and printer. + +\begin{lstClean} +class addition v where + add :: v a -> v a -> v a | + a + +instance addition Eval where + add (Eval l) (Eval r) = Eval (l + r) + +instance addition Printer where + add (Printer l) (Printer r) = Printer ("(" +++ l +++ "+" +++ r +++ ")") +\end{lstClean} + +Terms in our toy language can be overloaded in their interpretation, they are just an interface. +For example, $1+5$ is written as \cleaninline{add (lit 1) (lit 5)} and has the type \cleaninline{v Int \| literals, addition v}. +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. +Creating such a function, e.g.\ one that both prints and evaluates an expression, requires rank-2 polymorphism (see \cref{lst:rank2_mtask}). \section{Types} -To leverage the type checker of the host language, types in the \gls{MTASK} language are expressed as types in the host language, to make the language type safe. -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 therefore added to the \gls{DSL} functions. -The most used class constraint is the \cleaninline{type} class collection containing functions for serialization, printing, \gls{ITASK} constraints, \etc. -Many of these functions can be derived using generic programming. -An even stronger restriction on types is defined for types that have a stack representation. +The \gls{MTASK} language is a tagless-final \gls{EDSL} as well. +As it is shallowly embedded, the types of the terms in the language can be constrained by type classes. +Types in the \gls{MTASK} language are expressed as types in the host language, to make the language type safe. +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. +\Cref{tbl:mtask-c-datatypes} shows the mapping from \gls{CLEAN} types to \ccpp{} types. +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}. +Most of these functions are automatically derivable using generic programming but can be specialised when needed. +An even stronger restriction is defined for types that have a stack representation. This \cleaninline{basicType} class has instances for many \gls{CLEAN} basic types such as \cleaninline{Int}, \cleaninline{Real} and \cleaninline{Bool}. -The class constraints for values in \gls{MTASK} are omnipresent in all functions and therefore often omitted throughout throughout the chapters for brevity and clarity. +These class constraints for values in \gls{MTASK} are omnipresent in all functions and therefore usually omitted for brevity and clarity. \begin{table}[ht] \centering - \caption{Translation from \gls{CLEAN}/\gls{MTASK} data types to \gls{CPP} datatypes.}% + \caption{Translation from \cmtask{} data types to \ccpp{} datatypes.}% \label{tbl:mtask-c-datatypes} \begin{tabular}{lll} \toprule - \gls{CLEAN}/\gls{MTASK} & \gls{CPP} type & \textnumero{}bits\\ + \cmtask{} & \ccpp{} & \textnumero{}bits\\ \midrule - \cleaninline{Bool} & \cinline{bool} & 16\\ - \cleaninline{Char} & \cinline{char} & 16\\ - \cleaninline{Int} & \cinline{int16_t} & 16\\ - \cleaninline{Real} & \cinline{float} & 32\\ - \cleaninline{:: Long} & \cinline{int32_t} & 32\\ + \cleaninline{Bool} & \cinline{bool} & 16\\ + \cleaninline{Char} & \cinline{char} & 16\\ + \cleaninline{Int} & \cinline{int16_t}\footnotemark{} & 16\\ + \cleaninline{Real} & \cinline{float} & 32\\ + \cleaninline{:: Long} & \cinline{int32_t} & 32\\ \cleaninline{:: T = A \| B \| C} & \cinline{enum} & 16\\ \bottomrule \end{tabular} \end{table} +\footnotetext{In \gls{ARDUINO} \ccpp{} this usually equals a \cinline{long}.} \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. -The \gls{MTASK} language interface consists of a core collection of type classes bundled in the type class \cleaninline{class mtask}. -Every interpretation implements the type classes in the \cleaninline{mtask} class -There are also \gls{MTASK} extensions that not every interpretation implements such as peripherals and \gls{ITASK} integration. + \begin{lstClean}[caption={Classes and class collections for the \gls{MTASK} language.},label={lst:constraints}] -class type t | iTask, ... ,fromByteCode, toByteCode t +class type t | iTask, ... , fromByteCode, toByteCode t class basicType t | type t where ... +\end{lstClean} +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}). +Every interpretation of \gls{MTASK} terms implements the type classes in the \cleaninline{mtask} class collection. +%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. + +\begin{lstClean}[caption={Class collection for the \gls{MTASK} language.},label={lst:collection}] class mtask v | expr, ..., int, real, long v \end{lstClean} -Sensors, \glspl{SDS}, functions, \etc{} may only be defined at the top level. -The \cleaninline{Main} type is used that is used to distinguish the top level from the main expression. -Some top level definitions, such as functions, are defined using \gls{HOAS}. -To make their syntax friendlier, the \cleaninline{In} type---an infix tuple---is used to combine these top level definitions as can be seen in \cleaninline{someTask} (\cref{lst:mtask_types}). +Peripheral, \gls{SDS}, and function definitions are always defined at the top level of \gls{MTASK} programs. +This is enforced by the \cleaninline{Main} type. +Most top level definitions are defined using \gls{HOAS}. +To make their syntax friendlier, the \cleaninline{In} type---an infix tuple---is used to combine these top level definitions. +To illustrate the structure of a \gls{MTASK} programs, \cref{lst:mtask_types} shows a skeleton of a program. -\begin{lstClean}[caption={Example task and auxiliary types in the \gls{MTASK} language.},label={lst:mtask_types}] +% VimTeX: SynIgnore on +\begin{lstClean}[caption={Auxiliary types and example task in the \gls{MTASK} language.},label={lst:mtask_types}] +// From the mTask library :: Main a = { main :: a } :: In a b = (In) infix 0 a b -someTask :: MTask v Int | mtask v & liftsds v & sensor1 v & ... +someTask :: MTask v Int | mtask v & lowerSds v & sensor1 v & ... someTask = sensor1 config1 \sns1-> sensor2 config2 \sns2-> - sds \s1 = initialValue - In liftsds \s2 = someiTaskSDS - In fun \fun1= ( ... ) - In fun \fun2= ( ... ) + sds \s1 = initialValue + In lowerSds \s2 = someiTaskSDS + In fun \fun1= ( \(a0, a1)->... ) + In fun \fun2= ( \a->... ) In { main = mainexpr } \end{lstClean} +% VimTeX: SynIgnore off -\Gls{MTASK} expressions are usually overloaded in their interpretation (\cleaninline{v}). +Expressions in the \gls{MTASK} language are usually overloaded in their interpretation (\cleaninline{v}). In \gls{CLEAN}, all free variables in a type are implicitly universally quantified. -In order to use the \gls{MTASK} expressions with multiple interpretations, rank-2 polymorphism is required \citep{odersky_putting_1996}\citep[\citesection{3.7.4}]{plasmeijer_clean_2021}. +In order to use the \gls{MTASK} expressions with multiple interpretations, rank-2 polymorphism is required \citep{odersky_putting_1996}. \Cref{lst:rank2_mtask} shows an example of a function that simulates an \gls{MTASK} expression while showing the pretty printed representation in parallel. -Providing a type for the \cleaninline{simulateAndPrint} function is mandatory as the compiler cannot infer the type of rank-2 polymorphic functions. - -\begin{lstClean}[label={lst:rank2_mtask},caption={Rank-2 polymorphism to allow multiple interpretations}] -prettyPrint :: Main (MTask PrettyPrint a) -> String -simulate :: Main (MTask Simulate a) -> Task a +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}. +\begin{lstClean}[label={lst:rank2_mtask},caption={Rank-2 polymorphism to allow multiple interpretations.}] simulateAndPrint :: (A.v: Main (MTask v a) | mtask v) -> Task a | type a simulateAndPrint mt = simulate mt - -|| Hint "Current task:" @>> viewInformation [] (prettyPrint mt) + -|| Hint "Current task:" @>> viewInformation [] (showMain mt) \end{lstClean} \section{Expressions}\label{sec:expressions} -This section shows all \gls{MTASK} constructs for exppressions. -\Cref{lst:expressions} shows the \cleaninline{expr} class containing the functionality to lift values from the host language to the \gls{MTASK} language (\cleaninline{lit}); perform number and boolean arithmetics; do comparisons; and conditional execution. -For every common boolean and arithmetic operator in the host language, an \gls{MTASK} variant is present, suffixed by a period to not clash with \gls{CLEAN}'s builtin operators. - -\begin{lstClean}[caption={The \gls{MTASK} class for expressions},label={lst:expressions}] +This section shows all \gls{MTASK} language constructs for expressions. +\Cref{lst:expressions} shows the \cleaninline{expr} class containing the functionality to: +lift values from the host language to the \gls{MTASK} language (\cleaninline{lit}); +perform numeric and boolean arithmetics; +do comparisons; +and perform conditional execution. +For every common boolean and arithmetic operator in the host language, an \gls{MTASK} variant is present. +The operators are suffixed by a period to not clash with the built-in operators in \gls{CLEAN}. + +\begin{lstClean}[caption={The \gls{MTASK} class for expressions.},label={lst:expressions}] class expr v where lit :: t -> v t | type t (+.) infixl 6 :: (v t) (v t) -> v t | basicType, +, zero t @@ -133,7 +200,8 @@ class expr v where If :: (v Bool) (v t) (v t) -> v t | type t \end{lstClean} -Conversion to-and-fro data types is available through the overloaded functions \cleaninline{int}, \cleaninline{long} and \cleaninline{real} that will convert the argument to the respective type similar to casting in \gls{C}. +Conversion to and fro data types is available through the overloaded functions \cleaninline{int}, \cleaninline{long} and \cleaninline{real}. +These functions convert the argument to the respective type similar to casting in \gls{C}. For most interpretations, there are instances of these classes for all numeric types. \begin{lstClean}[caption={Type conversion functions in \gls{MTASK}.}] @@ -143,48 +211,48 @@ class long v a :: (v a) -> v Long \end{lstClean} Values from the host language must be explicitly lifted to the \gls{MTASK} language using the \cleaninline{lit} function. -For convenience, there are many lower-cased macro definitions for often used constants such as \cleaninline{true :== lit True}, \cleaninline{false :== lit False}, \etc. +For convenience, there are many lower-cased macro definitions for often-used constants such as \cleaninline{true :== lit True}, \cleaninline{false :== lit False}. -\Cref{lst:example_exprs} shows some examples of these expressions. +\Cref{lst:example_exprs} shows some examples of expressions in the \gls{MTASK} language. Since they are only expressions, there is no need for a \cleaninline{Main}. -\cleaninline{e0} defines the literal $42$, \cleaninline{e1} calculates the literal $42.0$ using real numbers. -\cleaninline{e2} compares \cleaninline{e0} and \cleaninline{e1} as integers and if they are equal it returns the \cleaninline{e2}$/2$ and \cleaninline{e0} otherwise. +\cleaninline{e0} defines the literal \num{42}, \cleaninline{e1} calculates the literal \num{42.0} using real numbers and uses a type conversion. +\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. \begin{lstClean}[label={lst:example_exprs},caption={Example \gls{MTASK} expressions.}] e0 :: v Int | expr v e0 = lit 42 e1 :: v Real | expr v -e1 = lit 38.0 + real (lit 4) +e1 = lit 38.0 +. real (lit 4) e2 :: v Int | expr v -e2 = if' (e0 ==. int e1) +e2 = If (e0 ==. int e1) (int e1 /. lit 2) e0 \end{lstClean} -\Gls{MTASK} is shallowly embedded in \gls{CLEAN} and the terms are constructed at runtime. -This means that \gls{MTASK} programs can also be tailor-made at runtime or constructed using \gls{CLEAN} functions maximising the linguistic reuse \citep{krishnamurthi_linguistic_2001} -\cleaninline{approxEqual} in \cref{lst:example_macro} performs a simple approximate equality---albeit without taking into account all floating point pecularities. -When calling \cleaninline{approxEqual} in an \gls{MTASK} function, the resulting code is inlined. - -\begin{lstClean}[label={lst:example_macro},caption={Example linguistic reuse in the \gls{MTASK} language.}] -approxEqual :: (v Real) (v Real) (v Real) -> v Real | expr v -approxEqual x y eps = if' (x ==. y) true - ( if' (x >. y) - (y -. x < eps) - (x -. y < eps) - ) +The \gls{MTASK} language is shallowly embedded in \gls{CLEAN} and the terms are constructed and hence compiled at run time. +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}. +The \cleaninline{approxEqual} function in \cref{lst:example_macro} is an example of this. +It performs a simple approximate equality---admittedly not taking into account all floating point peculiarities. +When calling \cleaninline{approxEqual} in an \gls{MTASK} expression, the resulting code is inlined. + +\begin{lstClean}[label={lst:example_macro},caption={Approximate equality as an example of linguistic reuse in \gls{MTASK}.}] +approxEqual :: (v Real) (v Real) (v Real) -> v Bool | expr v +approxEqual x y eps = x ==. y |. If (x >. y) + (x -. y <. eps) + (y -. x <. eps) \end{lstClean} \subsection{Data types} -Most of \gls{CLEAN}'s fixed-size basic types are mapped on \gls{MTASK} types. -However, it can be useful to have access to compound types as well. -All types in \gls{MTASK} must have a fixed size representation on the stack so sum types are not (yet) supported. -While it is possible to lift types using the \cleaninline{lit} function, you cannot do anything with the types besides passing them around but they are being produced by some parallel task combinators (see \cref{sssec:combinators_parallel}). -To be able to use types as first class citizens, constructors and field selectors are required (see \cref{chp:first-class_datatypes}). -\Cref{lst:tuple_exprs} shows the scaffolding for supporting tuples in \gls{MTASK}. -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. -Examples for using tuple can be found in \cref{sec:mtask_functions}. +Most of the fixed-size basic types from \gls{CLEAN} are mapped on \gls{MTASK} types (see \cref{tbl:mtask-c-datatypes}). +However, it is useful to have access to compound types as well. +All types in \gls{MTASK} have a fixed-size representation on the stack, so sum types are not (yet) supported. +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. +However, you cannot do anything with values of the types besides passing them around. +To be able to use types as first-class citizens, constructors, and field selectors or deconstructors are required (see \cref{chp:first-class_datatypes}). +\Cref{lst:tuple_exprs} shows the scaffolding required for supporting tuples in \gls{MTASK}. +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. +Examples of \gls{MTASK} programs using tuples are seen later in \cref{sec:mtask_functions}. \begin{lstClean}[label={lst:tuple_exprs},caption={Tuple constructor and field selectors in \gls{MTASK}.}] class tupl v where @@ -192,49 +260,55 @@ class tupl v where first :: (v (a, b)) -> v a | type a & type b second :: (v (a, b)) -> v b | type a & type b + tupopen :: ((v a, v b) -> v c) -> ((v (a, b)) -> v c) tupopen f :== \v->f (first v, second v) \end{lstClean} \subsection{Functions}\label{sec:mtask_functions} -Adding functions to the language is achieved by type class to the \gls{DSL}. +Adding functions to the language is achieved by one type class to the \gls{DSL}. 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}. -The \gls{MTASK} only allows first-order functions and does not allow partial function application. -This is restricted by using a multi-parameter type class where the first parameter represents the arguments and the second parameter the view. -By providing a single instance per function arity instead of providing one instance for all functions and using tuples for the arguments this constraint can be enforced. -Also, \gls{MTASK} only supports top-level functions which is enforced by the \cleaninline{Main} box. -The definition of the type class and the instances for an example interpretation (\cleaninline{:: Inter}) are as follows: +The \gls{MTASK} language only allows first-order functions and no partial function application. +To restrict this, a multi-parameter type class is used instead of a type class with one type variable. +The first parameter represents the shape of the arguments, the second parameter the interpretation. +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. +By using argument tuples to represent the arity of the function, partial function applications are eradicated. +The definition of the type class and some instances for the pretty printer are as follows: \begin{lstClean}[caption={Functions in \gls{MTASK}.}] class fun a v :: ((a -> v s) -> In (a -> v s) (Main (MTask v u))) -> Main (MTask v u) -instance fun () Inter where ... -instance fun (Inter a) Inter | type a where ... -instance fun (Inter a, Inter b) Inter | type a, type b where ... -instance fun (Inter a, Inter b, Inter c) Inter | type a, ... where ... +instance fun () Show where ... +instance fun (Show a) Show | type a where ... +instance fun (Show a, Show b) Show | type a, type b where ... +instance fun (Show a, Show b, Show c) Show | type a, ... where ... ... \end{lstClean} -Deriving how to define and use functions from the type is quite the challenge even though the resulting syntax is made easier using the infix type \cleaninline{In}. -\Cref{lst:function_examples} show the factorial function, a tail-call optimised factorial function and a function with zero arguments to demonstrate the syntax. -Splitting out the function definition for each single arity means that that for every function arity and combination of arguments, a separate class constraint must be created. +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}. +\Cref{lst:function_examples} show some examples of functions to illustrate the syntax. +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. Many of the often used functions are already bundled in the \cleaninline{mtask} class constraint collection. -\cleaninline{factorialtail} shows a manually added class constraint. -Definiting zero arity functions is shown in the \cleaninline{zeroarity} expression. +The \cleaninline{factorial} functions shows a recursive version of the factorial function. +The \cleaninline{factorialtail} function is a tail-call optimised version of the factorial function. +It contains a manually added class constraint. +\todo[inline]{Uitleggen waarom je dit doet} +Zero-arity functions are always called with unit as an argument. +An illustration of this is seen in the \cleaninline{zeroarity} expression. Finally, \cleaninline{swapTuple} shows an example of a tuple being swapped. % VimTeX: SynIgnore on -\begin{lstClean}[label={lst:function_examples},caption={Function examples in \gls{MTASK}.}] +\begin{lstClean}[label={lst:function_examples},caption={Examples of various functions in \gls{MTASK}.}] factorial :: Main (v Int) | mtask v factorial = - fun \fac=(\i->if' (i <. lit 1) + fun \fac=(\i->If (i <. lit 1) (lit 1) (i *. fac (i -. lit 1))) In {main = fac (lit 5) } factorialtail :: Main (v Int) | mtask v & fun (v Int, v Int) v factorialtail = - fun \facacc=(\(acc, i)->if' (i <. lit 1) + fun \facacc=(\(acc, i)->If (i <. lit 1) acc (fac (acc *. i, i -. lit 1))) In fun \fac=(\i->facacc (lit 1, i)) @@ -254,18 +328,33 @@ swapTuple = % VimTeX: SynIgnore off \section{Tasks and task combinators}\label{sec:top} -This section describes \gls{MTASK}'s task language. -\Gls{MTASK}'s task language can be divided into three categories, namely -\begin{enumerate*} - \item Basic tasks, in most \gls{TOP} systems, the basic tasks are called editors, modelling the interactivity with the user. - In \gls{MTASK}, there are no \emph{editors} in that sense but there is interaction with the outside world through microcontroller peripherals such as sensors and actuators. - \item Task combinators provide a way of describing the workflow. +This section describes the task language of \gls{MTASK}. +\Gls{TOP} languages are programming languages enriched with tasks. +Tasks represent abstract pieces of work and can be combined using combinators. +Creating tasks is done by evaluating expressions. +The result of an evaluated task expression is called a task tree, a run time representation of a task. +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. +With each step, a task value is yielded that is observable by other tasks and can be acted upon. + +The implementation in the \gls{MTASK} \gls{RTS} for task execution is shown in \cref{chp:implementation}. +The following sections show the definitions of the functions for creating tasks. +They also show the semantics of tasks: their observable value in relation to the work that the task represents. +The task language of \gls{MTASK} is divided into three categories: + +\begin{description} + \item [Basic tasks] are the leaves in the task trees. + In most \gls{TOP} systems, the basic tasks are called editors, modelling the interactivity with the user. + In \gls{MTASK}, there are no editors in that sense. + Editors in \gls{MTASK} model the interaction with the outside world through peripherals such as sensors and actuators. + \item [Task combinators] provide a way of describing the workflow or collaboration. They combine one or more tasks into a compound task. - \item \glspl{SDS} in \gls{MTASK} can be seen as references to data that can be shared using many-to-many communication and are only accessible from within the task language to ensure atomicity. -\end{enumerate*} + \item [\Glspl{SDS}] are typed references to shared memory in \gls{MTASK}. + The data is available for tasks using many-to-many communication but only from within the task language to ensure atomicity. +\end{description} -As \gls{MTASK} is integrated with \gls{ITASK}, the same stability distinction is made for task values. -A task in \gls{MTASK} is denoted by the \gls{DSL} type synonym shown in \cref{lst:task_type}, an expression of the type \cleaninline{TaskValue a} in interpretation \cleaninline{v}. +As \gls{MTASK} is integrated with \gls{ITASK}, a stability distinction is made for task values just as in \gls{ITASK}. +A task in \gls{MTASK} is denoted by the \gls{DSL} type synonym shown in \cref{lst:task_type}. +A task is an expression of the type \cleaninline{TaskValue a} in interpretation \cleaninline{v}. \begin{lstClean}[label={lst:task_type},caption={Task type in \gls{MTASK}.}] :: MTask v a :== v (TaskValue a) @@ -277,59 +366,44 @@ A task in \gls{MTASK} is denoted by the \gls{DSL} type synonym shown in \cref{ls \end{lstClean} \subsection{Basic tasks} -The most rudimentary basic tasks are the \cleaninline{rtrn} and \cleaninline{unstable} tasks. -They lift the value from the \gls{MTASK} expression language to the task domain either as a stable value or an unstable value. +The \gls{MTASK} language contains interactive and non-interactive basic tasks. +As \gls{MTASK} is integrated in \gls{ITASK}, the same notion of stability is applied to the observable task value (see \cref{fig:taskvalue}). +\todo[inline]{Stability beter uitleggen?} +The most rudimentary non-interactive basic tasks in the task language of \gls{MTASK} are \cleaninline{rtrn} and \cleaninline{unstable}. +They lift the value from the \gls{MTASK} expression language to the task domain either as a stable or unstable value. There is also a special type of basic task for delaying execution. -The \cleaninline{delay} task---given a number of milliseconds---yields an unstable value while the time has not passed. +The \cleaninline{delay} task---parametrised by a number of milliseconds---yields an unstable value while the time has not passed. Once the specified time has passed, the time it overshot the planned time is yielded as a stable task value. See \cref{sec:repeat} for an example task using \cleaninline{delay}. -\begin{lstClean}[label={lst:basic_tasks},caption={Function examples in \gls{MTASK}.}] +\begin{lstClean}[label={lst:basic_tasks},caption={Non-interactive basic tasks in \gls{MTASK}.}] class rtrn v :: (v t) -> MTask v t class unstable v :: (v t) -> MTask v t class delay v :: (v n) -> MTask v n | long v n \end{lstClean} \subsubsection{Peripherals}\label{sssec:peripherals} -For every sensor or actuator, basic tasks are available that allow interaction with the specific peripheral. -The type classes for these tasks are not included in the \cleaninline{mtask} class collection as not all devices nor all language interpretations have such peripherals connected. - -\Cref{lst:dht,lst:gpio} show the type classes for \glspl{DHT} sensors and \gls{GPIO} access. -Other peripherals have similar interfaces, they are available in the \cref{sec:aux_peripherals}. -For the \gls{DHT} sensor there are two basic tasks, \cleaninline{temperature} and \cleaninline{humidity}, that---will given a \cleaninline{DHT} object---produce a task that yields the observed temperature in \unit{\celcius} or relative humidity as a percentage as an unstable value. -Currently, two different types of \gls{DHT} sensors are supported, the \emph{DHT} family of sensors connect through $1$-wire protocol and the \emph{SHT} family of sensors connected using \gls{I2C} protocol. -Creating such a \cleaninline{DHT} object is very similar to creating functions in \gls{MTASK} and uses \gls{HOAS} to make it type safe. - -\begin{lstClean}[label={lst:dht},caption={The \gls{MTASK} interface for \glspl{DHT} sensors.}] -:: DHT //abstract -:: DHTInfo - = DHT_DHT Pin DHTtype - | DHT_SHT I2CAddr -:: DHTtype = DHT11 | DHT21 | DHT22 -class dht v where - DHT :: DHTInfo ((v DHT) -> Main (v b)) -> Main (v b) | type b - temperature :: (v DHT) -> MTask v Real - humidity :: (v DHT) -> MTask v Real - -measureTemp :: Main (MTask v Real) | mtask v & dht v -measureTemp = DHT (DHT_SHT (i2c 0x36)) \dht-> - {main=temperature dht} -\end{lstClean} - -\Gls{GPIO} access is divided into three classes: analog, digital and pin modes (see \cref{lst:gpio}). -For all pins and pin modes an \gls{ADT} is available that enumerates the pins. -The analog \gls{GPIO} pins of a microcontroller are connected to an \gls{ADC} that translates the voltage to an integer. -Analog \gls{GPIO} pins can be either read or written to. -Digital \gls{GPIO} pins only report a high or a low value. -The \cleaninline{pin} type class allows functions to be overloaded in either the analog or digital pins, e.g.\ analog pins can be considered as digital pins as well. +In order for the edge device to interact with the environment, peripherals such as sensors and actuators are employed. +Some peripherals are available on the microcontroller package, others are connected with wires using protocols such as \gls{I2C}. +For every supported sensor or actuator, basic tasks are available that allow interaction with the specific peripheral. +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. + +An example of a built-in peripheral is the \gls{GPIO} system. +This array of digital and analogue pins is controlled through software. +\Gls{GPIO} access is divided into three classes: analogue \gls{IO}, digital \gls{IO} and pin mode configuration (see \cref{lst:gpio}). +For all pins and pin modes, an \gls{ADT} is available that enumerates the pins. +The analogue \gls{GPIO} pins of a microcontroller are connected to an \gls{ADC} that translates the measured voltage to an integer. +Digital \gls{GPIO} pins of a microcontroller report either a high or a low value. +Both analogue and digital \gls{GPIO} pins can be read or written to, but it is advised to set the pin mode accordingly. +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. For digital \gls{GPIO} interaction, the \cleaninline{dio} type class is used. The first argument of the functions in this class is overloaded, i.e.\ it accepts either an \cleaninline{APin} or a \cleaninline{DPin}. -Analog \gls{GPIO} tasks are bundled in the \cleaninline{aio} type class. +Analogue \gls{GPIO} tasks are bundled in the \cleaninline{aio} type class. \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. -This setting can be applied using the \cleaninline{pinMode} class by hand or by using the \cleaninline{declarePin} \gls{GPIO} pin constructor. -Setting the pin mode is a task that immediately finisheds and fields a stable unit value. -Writing to a pin is also a task that immediately finishes but yields the written value instead. +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. +Setting the pin mode is a task that immediately finishes and yields a stable unit value. +Writing to a pin is also a task that immediately finishes, but yields the written value. Reading a pin is a task that yields an unstable value---i.e.\ the value read from the actual pin. \begin{lstClean}[label={lst:gpio},caption={The \gls{MTASK} interface for \gls{GPIO} access.}] @@ -355,7 +429,7 @@ class pinMode v where \end{lstClean} \Cref{lst:gpio_examples} shows two examples of \gls{MTASK} tasks using \gls{GPIO} pins. -\cleaninline{task1} reads analog \gls{GPIO} pin 3. +\cleaninline{task1} reads analogue \gls{GPIO} pin 3. This is a task that never terminates. \cleaninline{task2} writes the \cleaninline{true} (\gls{ARDUINO} \arduinoinline{HIGH}) value to digital \gls{GPIO} pin 3. This task finishes immediately after writing to the pin. @@ -368,20 +442,46 @@ task2 :: MTask v Int | mtask v task2 = declarePin D3 PMOutput \d3->{main=writeD d3 true} \end{lstClean} +Peripherals are bundled by their functionality in \gls{MTASK}. +For example, \cref{lst:dht} shows the type classes for all supported \gls{DHT} sensors. +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. +Creating such a \cleaninline{DHT} object is very similar to creating functions in \gls{MTASK} and uses \gls{HOAS} to make it type safe. +When provided a configuration and a configuration function, the \gls{DHT} object can be brought into scope. +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. +Other peripherals have similar interfaces, they are available in \cref{sec:aux_peripherals}. + +\begin{lstClean}[label={lst:dht},caption={The \gls{MTASK} interface for \glspl{DHT} sensors.}] +:: DHT //abstract +:: DHTInfo + = DHT_DHT Pin DHTtype + | DHT_SHT I2CAddr +:: DHTtype = DHT11 | DHT21 | DHT22 +class dht v where + DHT :: DHTInfo ((v DHT) -> Main (v b)) -> Main (v b) | type b + temperature :: (v DHT) -> MTask v Real + humidity :: (v DHT) -> MTask v Real + +measureTemp :: Main (MTask v Real) | mtask, dht v +measureTemp = DHT (DHT_SHT (i2c 0x36)) \dht->{main=temperature dht} +\end{lstClean} + \subsection{Task combinators} Task combinators are used to combine multiple tasks to describe workflows. +The \gls{MTASK} language has a set of simpler combinators from which more complicated workflow can be derived. There are three main types of task combinators, namely: -\begin{enumerate*} - \item Sequential combinators that execute tasks one after the other, possibly using the result of the left hand side. - \item Parallel combinators that execute tasks at the same time combining the result. - \item Miscellaneous combinators that change the semantics of a task---e.g.\ repeat it or delay execution. -\end{enumerate*} +\begin{itemize} + \item sequential combinators that execute tasks one after the other, possibly using the result of the left-hand side; + \item parallel combinators that execute tasks at the same time, combining the result; + \item miscellaneous combinators that change the semantics of a task---for example a combinator that repeats the child task. +\end{itemize} \subsubsection{Sequential} -Sequential task combination allows the right-hand side task to observe the left-hand side task value. -All seqential task combinators are expressed in the \cleaninline{expr} class and can be---and are by default---derived from the Swiss army knife step combinator \cleaninline{>>*.}. +Sequential task combination allows the right-hand side expression to \emph{observe} the left-hand side task value. +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}). This combinator has a single task on the left-hand side and a list of \emph{task continuations} on the right-hand side. -The list of task continuations is checked every rewrite step and if one of the predicates matches, the task continues with the result of this combination. +Every rewrite step, the list of task continuations are tested on the task value. +If one of the predicates matches, the task continues with the result of these continuations. +Several shorthand combinators are derived from the step combinator. \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. \cleaninline{>>\|.} is a shorthand for the sequence operation, if the left-hand side is stable, it continues with the right-hand side task. The \cleaninline{>>~.} and \cleaninline{>>..} combinators are variants of the ones above that ignore the stability and continue on an unstable value as well. @@ -404,20 +504,19 @@ class step v | expr v where The following listing shows an example of a step in action. The \cleaninline{readPinBin} function produces an \gls{MTASK} task that classifies the value of an analogue pin into four bins. It also shows that the nature of embedding allows the host language to be used as a macro language. -Furthermore -\begin{lstClean}[label={lst:mtask_readpinbin},caption={Read an analog pin and bin the value in \gls{MTASK}.}] +\begin{lstClean}[label={lst:mtask_readpinbin},caption={Read an analogue pin and bin the value in \gls{MTASK}.}] readPinBin :: Int -> Main (MTask v Int) | mtask v readPinBin lim = declarePin PMInput A2 \a2-> { main = readA a2 >>*. [ IfValue (\x->x <. lim) (\_->rtrn (lit bin)) - \\ lim <-[64,128,192,256] + \\ lim <- [64,128,192,256] & bin <- [0..]]} \end{lstClean} \subsubsection{Parallel}\label{sssec:combinators_parallel} -The result of a parallel task combination is a compound that actually executes the tasks at the same time. -There are two types of parallel task combinators (see \cref{lst:mtask_parallel}). +The result of a parallel task combination is a compound task that executes both tasks at the same time. +There are two types of parallel task combinators in the \gls{MTASK} language (see \cref{lst:mtask_parallel}). \begin{lstClean}[label={lst:mtask_parallel},caption={Parallel task combinators in \gls{MTASK}.}] class (.&&.) infixr 4 v :: (MTask v a) (MTask v b) -> MTask v (a, b) @@ -428,7 +527,7 @@ The conjunction combinator (\cleaninline{.&&.}) combines the result by putting t The stability of the task depends on the stability of both children. If both children are stable, the result is stable, otherwise the result is unstable. The disjunction combinator (\cleaninline{.\|\|.}) combines the results by picking the leftmost, most stable task. -The semantics are most easily described using the \gls{CLEAN} functions shown in \cref{lst:semantics_con,lst:semantics_dis}. +The semantics of both parallel combinators are most easily described using the \gls{CLEAN} functions shown in \cref{lst:semantics_con,lst:semantics_dis}. \begin{figure}[ht] \centering @@ -450,55 +549,60 @@ dis :: (TaskValue a) (TaskValue a) dis NoValue r = r dis l NoValue = l dis (Value l ls) (Value r rs) - | rs = Value r True + | rs && not ls = Value r rs | otherwise = Value l ls \end{lstClean} \end{subfigure} \end{figure} -\Cref{lst:mtask_parallel_example} gives an example program using the parallel task combinator. -This program will read two pins at the same time, returning when one of the pins becomes \arduinoinline{HIGH}. -If the combinator was the \cleaninline{.&&.} instead, the type would be \cleaninline{MTask v (Bool, Bool)} and the task would only return when both pins have been \arduinoinline{HIGH} but not necessarily at the same time. +\Cref{lst:mtask_parallel_example} gives an example program that uses the parallel task combinator. +This task read two pins at the same time, returning when one of the pins becomes high. +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. \begin{lstClean}[label={lst:mtask_parallel_example},caption={Parallel task combinator example in \gls{MTASK}.}] task :: MTask v Bool task = declarePin D0 PMInput \d0-> declarePin D1 PMInput \d1-> - let monitor pin = readD pin >>*. [IfValue (\x->x) \x->rtrn x] + let monitor pin = readD pin >>*. [IfValue id rtrn] In {main = monitor d0 .||. monitor d1} \end{lstClean} \subsubsection{Repeat}\label{sec:repeat} -The \cleaninline{rpeat} combinator executes the child task. -If a stable value is observed, the task is reinstated. -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 in \cref{lst:blinkthreadmtask}. +In many workflows, tasks are to be executed repeatedly. +The \cleaninline{rpeat} combinator does this by executing the child task until it becomes stable. +Once a stable value is observed, the task is reinstated. +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}. \begin{lstClean}[label={lst:mtask_repeat},caption={Repeat task combinators in \gls{MTASK}.}] class rpeat v where rpeat :: (MTask v a) -> MTask v a \end{lstClean} -To demonstrate the combinator, \cref{lst:mtask_repeat_example} show \cleaninline{rpeat} in use. -This task will mirror the value read from analog \gls{GPIO} pin \cleaninline{A1} to pin \cleaninline{A2} by constantly reading the pin and writing the result. +\Cref{lst:mtask_repeat_example} shows an example of a task that uses the \cleaninline{rpeat} combinator. +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. -\begin{lstClean}[label={lst:mtask_repeat_example},caption={Exemplary repeat task in \gls{MTASK}.}] +\begin{lstClean}[label={lst:mtask_repeat_example},caption={Repeat task combinator example in \gls{MTASK}.}] task :: MTask v Int | mtask v -task = - declarePin A1 PMInput \a1-> - declarePin A2 PMOutput \a2-> - {main = rpeat (readA a1 >>~. writeA a2 >>|. delay (lit 1000))} +task = declarePin A1 PMInput \a1-> + declarePin A2 PMOutput \a2-> + {main = rpeat (readA a1 >>~. writeA a2 >>|. delay (lit 1000))} \end{lstClean} -\subsection{\texorpdfstring{\Glsxtrlongpl{SDS}}{Shared data sources}} +\subsection{Shared data sources} +For some collaborations it is cumbersome to only communicate data using task values. +\Glspl{SDS} are a safe abstraction over any data that fill this gap. +It allows tasks to safely and atomically read, write and update data stores in order to share data with other tasks. \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}). -Similar to peripherals (see \cref{sssec:peripherals}), they are constructed at the top level and are accessed through interaction tasks. +There are no combinators or user-defined \gls{SDS} types in \gls{MTASK} as there are in \gls{ITASK}. +Similar to peripherals and functions, \glspl{SDS} are defined at the top level with the \cleaninline{sds} function. +They are accessed through interaction tasks. The \cleaninline{getSds} task yields the current value of the \gls{SDS} as an unstable value. This behaviour is similar to the \cleaninline{watch} task in \gls{ITASK}. Writing a new value to \pgls{SDS} is done using \cleaninline{setSds}. This task yields the written value as a stable result after it is done writing. -Getting and immediately after 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. -To circumvent this issue, \cleaninline{updSds} is created, this task atomically updates the value of the \gls{SDS}. +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. +To circumvent this issue, \cleaninline{updSds} is available by which \pgls{SDS} can be atomically updated. The \cleaninline{updSds} task only guarantees atomicity within \gls{MTASK}. \begin{lstClean}[label={lst:mtask_sds},caption={\Glspl{SDS} in \gls{MTASK}.}] @@ -510,30 +614,95 @@ class sds v where updSds :: (v (Sds t)) ((v t) -> v t) -> MTask v t \end{lstClean} -\Cref{lst:mtask_sds_examples} shows an example using \glspl{SDS}. +\Cref{lst:mtask_sds_examples} shows an example task that uses \glspl{SDS}. 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}. -In the \cleaninline{main} expression, this function is called twice and the results are combined using the parallel or combinator (\cleaninline{.\|\|.}). -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.\todo{beter opschrijven} +In the \cleaninline{main} expression, this function is called twice with a different argument. +The results are combined using the parallel disjunction combinator (\cleaninline{.\|\|.}). +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. \begin{lstClean}[label={lst:mtask_sds_examples},caption={Examples with \glspl{SDS} in \gls{MTASK}.}] task :: MTask v Int | mtask v -task = declarePin D3 PMInput \d3-> +task = + declarePin D3 PMInput \d3-> declarePin D5 PMInput \d5-> sds \share=0 In fun \count=(\pin-> readD pin - >>* [IfValue (\x->x) (\_->updSds (\x->x +. lit 1) share)] + >>* [IfValue id (\_->updSds (\x->x +. lit 1) share)] >>| delay (lit 100) // debounce >>| count pin) In {main=count d3 .||. count d5} \end{lstClean} +\section{Interpretations} +\todo[inline]{Iets meer uitleg over waarom dit zo handig is} +This section describes all the interpretations that the \gls{MTASK} language has to offer. +Not all these interpretations are necessarily \gls{TOP} engines, i.e.\ not all the interpretations execute the resulting tasks. +Some may perform an analysis over the program or typeset the program. + +\subsection{Pretty printer} +The pretty printer interpretation converts an \gls{MTASK} term to a string representation. +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. +The only function exposed for this interpretation is the \cleaninline{showMain} function (\cref{lst:showmain}). +It runs the pretty printer and returns a list of strings containing the pretty printed result. +The pretty printing function does the best it can but obviously cannot reproduce layout, curried functions, and variable names. +This shortcoming is illustrated by printing a blink task that contains a function and currying in \cref{lst:showexample}. + +\begin{lstClean}[caption={The entry point for the pretty printing interpretation.},label={lst:showmain}] +:: Show a // from the mTask pretty printing library +showMain :: (Main (Show a)) -> [String] +\end{lstClean} + +\begin{lstClean}[caption={Pretty printing interpretation example.},label={lst:showexample}] +blinkTask :: Main (MTask v Bool) | mtask v +blinkTask = + fun \blink=(\state-> + writeD d13 state >>|. delay (lit 500) >>=. blink o Not + ) In {main = blink true} + +// output when printing: +// fun f0 a1 = writeD(D13, a1) >>= \a2.(delay 1000) +// >>| (f0 (Not a1)) in (f0 True) +\end{lstClean} + +\subsection{Simulator} +The simulator converts the expression to a ready-for-work \gls{ITASK} simulation. +There is one entry point for this interpretation (see \cref{lst:simulatemain}). +The task resulting from the \cleaninline{simulate} function presents the user with an interactive simulation environment (see \cref{fig:sim}). +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}. + +\begin{lstClean}[caption={The entry point for the simulation interpretation.},label={lst:simulatemain}] +:: TraceTask a // from the mTask simulator library +simulate :: (Main (TraceTask a)) -> [String] +\end{lstClean} + +\begin{figure} + \centering + \includegraphics[width=\linewidth]{simg} + \caption{Simulator interface for a blink program written in \gls{MTASK}.}\label{fig:sim} +\end{figure} + +\subsection{Byte code compiler} +The main interpretation of the \gls{MTASK} system is the byte code compiler (\cleaninline{:: BCInterpret a}). +This interpretation compiles the \gls{MTASK} term at run time to byte code. +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. +Furthermore, with a special language construct, \glspl{SDS} can be shared between \gls{MTASK} and \gls{ITASK} programs as well. +The integration with \gls{ITASK} is explained thoroughly later in \cref{chp:integration_with_itask}. + +The \gls{MTASK} language together with \gls{ITASK} is a heterogeneous \gls{DSL}. +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. +The \gls{MTASK} language is a \gls{TOP} language with basic tasks tailored for \gls{IOT} edge devices (see \cref{sec:top}). +It uses expressions based a simply-typed $\lambda$-calculus with support for some basic types, arithmetic operations, and function definitions. + \section{Conclusion} -\Gls{MTASK} is a rich \gls{TOP} language tailored for \gls{IOT} systems. -It is embedded in the pure functional language \gls{CLEAN} and uses an enriched lambda calculus as a host language. -It provides support for all common arithmetic expressions, conditionals, functions, but also several basic tasks, task combinators, peripheral support and integration with \gls{ITASK}. -By embedding domain-specific knowledge in the language itself, it achieves the same abstraction level and dynamicity as other \gls{TOP} languages while targetting tiny computers instead. -As a result, a single declarative specification can describe an entire \gls{IOT} system. +This chapter gave an overview of the complete \gls{MTASK} \gls{DSL}. +The \gls{MTASK} language is a rich \gls{TOP} language tailored for \gls{IOT} edge devices. +The language is implemented as a class-based shallowly \gls{EDSL} in the pure functional host language \gls{CLEAN}. +The language is an enriched lambda calculus as a host language. +It provides language constructs for arithmetic expressions, conditionals, functions, but also non-interactive basic tasks, task combinators, peripheral support, and integration with \gls{ITASK}. +Terms in the language are just interfaces and can be interpreted by one or more interpretations. +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. +This facilitates tailor-making tasks for the current work requirements. \input{subfilepostamble} \end{document}