X-Git-Url: https://git.martlubbers.net/?a=blobdiff_plain;f=top%2Flang.tex;h=a248aa52bd1b8da65035d92706b97a96355ebda9;hb=20d972f3af3f073519e6e3e23edf4eb725f8f52d;hp=d0945c9591aec48b49b1e6c0f29e49a86d649d0f;hpb=29cb219e56ad3b238d81be2f71205120f689375e;p=phd-thesis.git diff --git a/top/lang.tex b/top/lang.tex index d0945c9..a248aa5 100644 --- a/top/lang.tex +++ b/top/lang.tex @@ -2,37 +2,47 @@ \input{subfilepreamble} -\setcounter{chapter}{3} +\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 introducing class-based shallow embedding and the setup of the \gls{MTASK} language; \item describing briefly the various interpretations; - \item and showing the language interface for: - \begin{itemize} - \item the types; - \item expressions, datatypes, and functions; - \item tasks and task combinators; - \item and \glspl{SDS}. - \end{itemize} + \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. -Due to the nature of the embedding technique, it is possible to have multiple interpretations for programs written in the \gls{MTASK} language. -As the language is implemented as an \gls{EDSL} in \gls{CLEAN} using class-based---or tagless-final---embedding (see \cref{sec:tagless-final_embedding}). -This means that the language is a collection of type classes and interpretations are data types implementing these classes. -Consequently, the language is extensible both in language constructs and in intepretations. -Adding a language construct is as simple as adding a type class and adding an interpretation is done by creating a new data type and providing implementations for the various type classes. -Let us illustrate this by taking the very simple language of literal values. +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 a values, as long as it has a \cleaninline{toString} instance, from the host language to our new \gls{DSL}. +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 @@ -40,7 +50,7 @@ class literals v where \end{lstClean} 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 could also be some monadic computation. +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. \begin{lstClean} :: Eval a = Eval a @@ -52,8 +62,8 @@ instance literals Eval where lit a = Eval a \end{lstClean} -Extending our language with a printer happens by defining a new data type and providing instances for the type constructor classes. -The printer stores a printed representation and hence the type is just a phantom type. +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 @@ -65,8 +75,8 @@ instance literals Printer where lit a = Printer (toString a) \end{lstClean} -Finally, adding language constructs happens by defining new type classes and giving implementations for some of the interpretations. -The following listing adds an addition construct to the language and shows implementations for the evaluator and printer. +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 @@ -79,146 +89,105 @@ instance addition Printer where add (Printer l) (Printer r) = Printer ("(" +++ l +++ "+" +++ r +++ ")") \end{lstClean} -Terms in our little toy language can be overloaded in their interpretation, they are just an interface. +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 polymorphism is implemented in most functional languages, it is not always straightforward to use multiple interpretations in one function. +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{Interpretations} -This section describes all \gls{MTASK}'s interpretations. -Not all of these interpretations are necessarily \gls{TOP} engines, i.e.\ not all of the interpretations execute the terms/tasks. -Some may perform an analysis over the program or typeset the program so that a textual representation can be shown. - -\subsection{Pretty printer} -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. -The only function exposed for this interpretation is the \cleaninline{showMain} (\cref{lst:showmain}) function. -It runs the pretty printer and returns a list of strings containing the pretty printed result as shown in \cref{lst:showexample}. -The pretty printing function does the best it can but obviously cannot reproduce the layout, curried functions and variable names. -This shortcoming is illustrated by the example application for blinking a single \gls{LED} using a function and currying in \cref{lst:showexample}. - -\begin{lstClean}[caption={The entrypoint for the pretty printing interpretation.},label={lst:showmain}] -:: Show a // from the mTask Show library -showMain :: (Main (Show a)) -> [String] | type a -\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: -// 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 in which the user can inspect and control the simulated peripherals and see the internal state of the tasks. -The task resulting from the \cleaninline{simulate} function presents the user with an interactive simulation environment (see \cref{lst:simulatemain,fig:sim}). -From within the interactive application, tasks can be (partly) executed, peripheral states changed and \glspl{SDS} interacted with. - -\begin{lstClean}[caption={The entrypoint for the simulation interpretation.},label={lst:simulatemain}] -:: TraceTask a // from the mTask Show library -simulate :: (Main (TraceTask a)) -> [String] | type a -\end{lstClean} - -\begin{figure} - \centering - \includegraphics[width=\linewidth]{simg} - \caption{Simulator interface for the blink program.}\label{fig:sim} -\end{figure} - -\subsection{Byte code compiler} -The main interpretation of the \gls{MTASK} system is the byte code compiler. -With it, and a handful of integration functions and tasks, \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. -This interface is explained thoroughly in \cref{chp:integration_with_itask}. - -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---for example 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}). - \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}\slash\gls{MTASK} data types to \ccpp{} datatypes.}% + \caption{Translation from \cmtask{} data types to \ccpp{} datatypes.}% \label{tbl:mtask-c-datatypes} \begin{tabular}{lll} \toprule - \gls{CLEAN}\slash\gls{MTASK} & \ccpp{} 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 @@ -231,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}.}] @@ -241,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 @@ -290,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)) @@ -352,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) @@ -375,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.}] @@ -453,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. @@ -466,21 +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. -In contrast to \gls{ITASK}, that uses super combinators to derive the simpler ones, \gls{MTASK} has a set of simpler combinators from which more complicated workflow can be derived. +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. @@ -503,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) @@ -527,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 @@ -549,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}.}] @@ -609,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}