From 4c449b205b49b4773934bd5cfd22e0f15e199eeb Mon Sep 17 00:00:00 2001 From: Mart Lubbers Date: Wed, 7 Dec 2022 16:09:29 +0100 Subject: [PATCH] more updates --- appx/bytecode.tex | 3 +- appx/c4hp.tex | 3 +- appx/mtask_aux.tex | 3 +- asbook.tex | 8 + back/acknowledgements.tex | 3 +- back/curriculum_vitae.tex | 3 +- back/research_data_management.tex | 3 +- back/samenvatting.tex | 3 +- back/summary.tex | 3 +- concl/concl.tex | 3 +- dsl/class.tex | 3 +- dsl/first.tex | 4 +- front/dedication.tex | 3 +- front/motto.tex | 3 +- front/titlepage.tex | 3 +- intro/intro.tex | 46 ++- preamble.tex | 16 +- self.bib | 53 ++- subfilepostamble.tex | 6 +- subfilepreamble.tex | 1 + subfileprefix.tex | 6 +- top/4iot.tex | 11 +- top/green.tex | 1 - top/imp.tex | 571 +++++++++++++++++++++++++++++- top/int.tex | 5 +- top/lang.tex | 6 +- tvt/tvt.tex | 136 ++++--- 27 files changed, 769 insertions(+), 140 deletions(-) create mode 100644 asbook.tex diff --git a/appx/bytecode.tex b/appx/bytecode.tex index 0eeab6e..594e630 100644 --- a/appx/bytecode.tex +++ b/appx/bytecode.tex @@ -1,8 +1,9 @@ \documentclass[../thesis.tex]{subfiles} -\include{subfilepreamble} +\input{subfilepreamble} \begin{document} +\input{subfileprefix} \chapter{Bytecode instruction set}% \label{chp:bytecode_instruction_set}% \todo[inline]{formatting} diff --git a/appx/c4hp.tex b/appx/c4hp.tex index aeba2dc..cfb27bc 100644 --- a/appx/c4hp.tex +++ b/appx/c4hp.tex @@ -1,8 +1,9 @@ \documentclass[../thesis.tex]{subfiles} -\include{subfilepreamble} +\input{subfilepreamble} \begin{document} +\input{subfileprefix} \chapter{\texorpdfstring{\glsentrytext{CLEAN}}{Clean} for \texorpdfstring{\glsentrytext{HASKELL}}{Haskell} Programmers}% \label{chp:clean_for_haskell_programmers} diff --git a/appx/mtask_aux.tex b/appx/mtask_aux.tex index fc43da7..019e0cf 100644 --- a/appx/mtask_aux.tex +++ b/appx/mtask_aux.tex @@ -1,8 +1,9 @@ \documentclass[../thesis.tex]{subfiles} -\include{subfilepreamble} +\input{subfilepreamble} \begin{document} +\input{subfileprefix} \chapter{Auxiliary \texorpdfstring{\glsentrytext{MTASK}}{mTask} type classes}% \label{chp:mtask_aux} \lstset{basicstyle=\tt\footnotesize} diff --git a/asbook.tex b/asbook.tex new file mode 100644 index 0000000..f7228ca --- /dev/null +++ b/asbook.tex @@ -0,0 +1,8 @@ +\documentclass[a4paper]{minimal} + +\usepackage{geometry} +\usepackage{pdfpages} + +\begin{document} +\includepdf[landscape,booklet,pages={1-18}]{thesis.pdf}%chktex 29 chktex 8 +\end{document} diff --git a/back/acknowledgements.tex b/back/acknowledgements.tex index b0eed01..d2e862e 100644 --- a/back/acknowledgements.tex +++ b/back/acknowledgements.tex @@ -1,8 +1,9 @@ \documentclass[../thesis.tex]{subfiles} -\include{subfilepreamble} +\input{subfilepreamble} \begin{document} +\input{subfileprefix} \chapter{Acknowledgements}% \label{chp:acknowledgements} %\begin{center} diff --git a/back/curriculum_vitae.tex b/back/curriculum_vitae.tex index 0e81df1..23977d4 100644 --- a/back/curriculum_vitae.tex +++ b/back/curriculum_vitae.tex @@ -1,8 +1,9 @@ \documentclass[../thesis.tex]{subfiles} -\include{subfilepreamble} +\input{subfilepreamble} \begin{document} +\input{subfileprefix} \chapter{Curriculum Vit\ae}% \label{chp:curriculum_vitae} Mart Lubbers diff --git a/back/research_data_management.tex b/back/research_data_management.tex index 1481159..e1ec602 100644 --- a/back/research_data_management.tex +++ b/back/research_data_management.tex @@ -1,8 +1,9 @@ \documentclass[../thesis.tex]{subfiles} -\include{subfilepreamble} +\input{subfilepreamble} \begin{document} +\input{subfileprefix} \chapter{Research Data Management}% \label{chp:research_data_management} diff --git a/back/samenvatting.tex b/back/samenvatting.tex index 22c3c9d..aa9d1cf 100644 --- a/back/samenvatting.tex +++ b/back/samenvatting.tex @@ -1,8 +1,9 @@ \documentclass[../thesis.tex]{subfiles} -\include{subfilepreamble} +\input{subfilepreamble} \begin{document} +\input{subfileprefix} \chapter{Samenvatting}% \label{chp:samenvatting} \selectlanguage{dutch} diff --git a/back/summary.tex b/back/summary.tex index 38db72d..08e0fd6 100644 --- a/back/summary.tex +++ b/back/summary.tex @@ -1,8 +1,9 @@ \documentclass[../thesis.tex]{subfiles} -\include{subfilepreamble} +\input{subfilepreamble} \begin{document} +\input{subfileprefix} \chapter{Summary}% \label{chp:summary} \begin{center} diff --git a/concl/concl.tex b/concl/concl.tex index 79e18f2..48e7abd 100644 --- a/concl/concl.tex +++ b/concl/concl.tex @@ -1,8 +1,9 @@ \documentclass[../thesis.tex]{subfiles} -\include{subfilepreamble} +\input{subfilepreamble} \begin{document} +\input{subfileprefix} \chapter{Coda}% \label{chp:conclusion} \todo{Or finale} diff --git a/dsl/class.tex b/dsl/class.tex index 542403b..e710142 100644 --- a/dsl/class.tex +++ b/dsl/class.tex @@ -3,11 +3,10 @@ \input{subfilepreamble} \begin{document} +\input{subfileprefix} \chapter{Deep embedding with class}% \label{chp:classy_deep_embedding} -\input{subfileprefix} - \begin{chapterabstract} The two flavours of \gls{DSL} embedding are shallow and deep embedding. In functional languages, shallow embedding models the language constructs as functions in which the semantics are embedded. diff --git a/dsl/first.tex b/dsl/first.tex index 33e7478..21c8489 100644 --- a/dsl/first.tex +++ b/dsl/first.tex @@ -1,10 +1,10 @@ \documentclass[../thesis.tex]{subfiles} -\include{subfilepreamble} +\input{subfilepreamble} \begin{document} +\input{subfileprefix} \chapter{First-class data types in shallow \texorpdfstring{embedded domain-specific languages}{\glsxtrlongpl{EDSL}} using metaprogramming}% -\chaptermark{bork}% \label{chp:first-class_datatypes}% \begin{chapterabstract} \Gls{FP} languages are excellent for hosting \glspl{EDSL} because of their rich type systems, minimal syntax, and referential transparency. diff --git a/front/dedication.tex b/front/dedication.tex index 6aa934d..ff433bf 100644 --- a/front/dedication.tex +++ b/front/dedication.tex @@ -1,8 +1,9 @@ \documentclass[../thesis.tex]{subfiles} -\include{subfilepreamble} +\input{subfilepreamble} \begin{document} +\input{subfileprefix} \topskip0pt \vspace*{\fill} diff --git a/front/motto.tex b/front/motto.tex index 32d5e51..3f12574 100644 --- a/front/motto.tex +++ b/front/motto.tex @@ -1,8 +1,9 @@ \documentclass[../thesis.tex]{subfiles} -\include{subfilepreamble} +\input{subfilepreamble} \begin{document} +\input{subfileprefix} \topskip0pt \vspace*{\fill} diff --git a/front/titlepage.tex b/front/titlepage.tex index a44004b..bf5d021 100644 --- a/front/titlepage.tex +++ b/front/titlepage.tex @@ -1,8 +1,9 @@ \documentclass[../thesis.tex]{subfiles} -\include{subfilepreamble} +\input{subfilepreamble} \begin{document} +\input{subfileprefix} %\hypersetup{pageanchor=false} \begin{titlepage} \begin{center} diff --git a/intro/intro.tex b/intro/intro.tex index fcf191b..ceaea49 100644 --- a/intro/intro.tex +++ b/intro/intro.tex @@ -4,6 +4,7 @@ \setcounter{chapter}{-1} \begin{document} +\input{subfileprefix} \chapter{Prelude}% \label{chp:introduction} \begin{chapterabstract} @@ -279,31 +280,35 @@ This feather-light domain-specific \gls{OS} is written in portable \gls{C} with \Gls{MTASK} is seamlessly integrated with \gls{ITASK}: \gls{MTASK} tasks are integrated in such a way that they function as \gls{ITASK} tasks, and \glspl{SDS} in on the device can tether an \gls{ITASK} \gls{SDS}. Using \gls{MTASK}, the programmer can define all layers of an \gls{IOT} system as a single declarative specification. -\Cref{lst:intro_blink,fig:intro_blink} shows the code and a screenshot of an interactive \gls{MTASK}\slash{}\gls{ITASK} application for blinking \pgls{LED} on the microcontroller every user-specified interval. +\Cref{lst:intro_blink,fig:intro_blink} shows the code and a screenshot of an interactive \imtask{} application for blinking \pgls{LED} on the microcontroller every user-specified interval. \Crefrange{lst:intro:itask_fro}{lst:intro:itask_to} show the \gls{ITASK} part. First \pgls{SDS} is defined to communicate the blinking interval, then the \gls{MTASK} is connected using \cleaninline{withDevice}. Once connected, the \cleaninline{intBlink} task is sent to the device (\cref{lst:intro_liftmtask}) and, in parallel, an editor is shown that updates the value of the interval \gls{SDS} (\cref{lst:intro_editor}). -The \cleaninline{intBlink} task (\crefrange{lst:intro:mtask_fro}{lst:intro:mtask_to}) is the \gls{MTASK} part of the application. -It has its own tasks, \glspl{SDS}, and \gls{UOD}. -This task first defines \gls{GPIO} pin 13 to be of the output type (\cref{lst:intro:declarePin}), followed by lifting the \gls{ITASK} \gls{SDS} to an \gls{MTASK} \gls{SDS} (\cref{lst:intro:liftsds}). -The main expression of the program calls the \cleaninline{blink} function with an initial state. -This function on \crefrange{lst:intro:blink_fro}{lst:intro:blink_to} first reads the interval \gls{SDS}, waits the specified delay, writes the state to the \gls{GPIO} pin and calls itself recursively using the inverse of the state. - -\begin{figure} - \centering - \includegraphics[width=.3\textwidth]{blink} - \caption{Screenshot for the interactive blink application.}% - \label{fig:intro_blink} -\end{figure} -\begin{lstClean}[numbers=left,caption={\Gls{MTASK}\slash{}\gls{ITASK} interactive blinking.},label={lst:intro_blink}] +\begin{minipage}{.5\textwidth} +\begin{lstClean}[numbers=left,caption={\Imtask{} interactive blinking.},label={lst:intro_blink}] interactiveBlink :: Task Int[+\label{lst:intro:itask_fro}+] interactiveBlink = withShared 500 \iInterval->[+\label{lst:intro_withshared}+] withDevice {TCPSettings | host = ..., port = ...} \dev-> liftmTask (intBlink iInterval) dev[+\label{lst:intro_liftmtask}+] -|| Hint "Interval (ms)" @>> updateSharedInformation [] iInterval[+\label{lst:intro_editor}+][+\label{lst:intro:itask_to}+] +\end{lstClean} +\end{minipage}% +\begin{minipage}{.5\textwidth} + \centering + \includegraphics[width=.3\textwidth]{blink} + \captionof{figure}{Screenshot for the interactive blink application.}% + \label{fig:intro_blink} +\end{minipage} +The \cleaninline{intBlink} task (\cref{lst:intro_blink_mtask}) is the \gls{MTASK} part of the application. +It has its own tasks, \glspl{SDS}, and \gls{UOD}. +This task first defines \gls{GPIO} pin 13 to be of the output type (\cref{lst:intro:declarePin}), followed by lifting the \gls{ITASK} \gls{SDS} to an \gls{MTASK} \gls{SDS} (\cref{lst:intro:liftsds}). +The main expression of the program calls the \cleaninline{blink} function with an initial state. +This function on \crefrange{lst:intro:blink_fro}{lst:intro:blink_to} first reads the interval \gls{SDS}, waits the specified delay, writes the state to the \gls{GPIO} pin and calls itself recursively using the inverse of the state. + +\begin{lstClean}[numbers=left,caption={\Gls{MTASK} part of the interactive blinking application.},label={lst:intro_blink_mtask}] intBlink :: Shared sds Int -> MTask v Int | mtask, liftsds v & RWShared sds[+\label{lst:intro:mtask_fro}+] intBlink iInterval = declarePin D13 PMOutput \d13->[+\label{lst:intro:declarePin}+] @@ -341,6 +346,15 @@ This episode is paper based and contains the following papers: Besides showing the result, the paper also serves as a gentle introduction to, and contains a thorough literature study on \glsxtrlong{TH}. \end{enumerate} +\paragraph{Other publications on \texorpdfstring{\glspl{EDSL}}{eDSLs}:} +Furthermore, I co-authored another paper that is worth mentioning but did not really fit in this dissertation. + +\begin{enumerate}[resume] + \item \emph{Strongly-Typed Multi-View Stack-Based Computations} \citep{koopman_strongly-typed_2022} shows how to create type-safe \glspl{EDSL} representing stack-based computations. + Instead of encoding the arguments to a function as arguments in the host language, stack-based approaches use a run time stack that contains the arguments. + By encoding the required contents of the stack in the types, such systems can be made type safe. +\end{enumerate} + \paragraph{Contribution:} The research in these papers and writing the paper was performed by me, though there were weekly meetings with Pieter Koopman and Rinus Plasmeijer in which we discussed and refined the ideas for paper~\ref{enum:first-class}. @@ -398,13 +412,13 @@ This chapter is based on the conference paper and a journal paper extending it: \begin{enumerate}[resume] \item \emph{Tiered versus Tierless IoT Stacks: Comparing Smart Campus Software Architectures} \citep{lubbers_tiered_2020}\footnote{This work was partly funded by the 2019 Radboud-Glasgow Collaboration Fund.}\label{enum:iot20} compares traditional tiered programming to tierless architectures by comparing two implementations of a smart-campus application. \item \emph{Could Tierless Programming Reduce IoT Development Grief?} \citep{lubbers_could_2022} - is an extended version of the paper~\ref{enum:iot20}. + is an extended version of paper~\ref{enum:iot20}. It compares programming traditional tiered architectures to tierless architectures by illustrating a qualitative and a quantitative four-way comparison of a smart-campus application. \end{enumerate} \paragraph{Contribution:} Writing the paper was performed by all authors. -I created the server application, the \gls{CLEAN}\slash{}\gls{ITASK}\slash{}\gls{MTASK} implementation (\glsxtrshort{CWS}), and the \gls{CLEAN}\slash{}\gls{ITASK} implementation (\glsxtrshort{CRS}); +I created the server application, the \cimtask{} implementation (\glsxtrshort{CWS}), and the \citask{} implementation (\glsxtrshort{CRS}); Adrian Ramsingh created the \gls{MICROPYTHON} implementation (\glsxtrshort{PWS}); the original \gls{PYTHON} implementation (\glsxtrshort{PRS}), and the server application were created by \citet{hentschel_supersensors:_2016}. \input{subfilepostamble} diff --git a/preamble.tex b/preamble.tex index 2606a8c..d5a5322 100644 --- a/preamble.tex +++ b/preamble.tex @@ -155,7 +155,7 @@ % Graphics \usepackage{graphicx} % Images \graphicspath{{img/},{intro/img},{top/img},{tvt/img}} -\usepackage{caption} % subfigures +\usepackage{caption} % subfigures/captionof \usepackage{subcaption} \usepackage{rotating} \newcommand{\orcid}[1]{\href{https://orcid.org/#1}{\hspace{1mm}\includegraphics[width=1em]{orcid}\hspace{2mm} https://orcid.org/#1}} @@ -353,3 +353,17 @@ \newcommand{\requiresGHCmod}[2][]{\footnote{Requires \GHCmod{#2} to be enabled. #1}} \newcommand{\rplasmeijer}{Plasmeijer, prof.\ dr.\ ir.\ R.\ (Radboud University)} \newcommand{\erasmusplus}{ERASMUS\raisebox{.25ex}{+}} +\newcommand{\imtask}{\gls{ITASK}\slash\gls{MTASK}} +\newcommand{\Imtask}{\Gls{ITASK}\slash\gls{MTASK}} +\newcommand{\citask}{\gls{CLEAN}\slash\gls{ITASK}} +\newcommand{\Citask}{\Gls{CLEAN}\slash\gls{ITASK}} +\newcommand{\cimtask}{\gls{CLEAN}\slash\gls{ITASK}\slash\gls{MTASK}} +\newcommand{\Cimtask}{\Gls{CLEAN}\slash\gls{ITASK}\slash\gls{MTASK}} +\newcommand{\ccpp}{\gls{C}\slash\gls{CPP}} +\newcommand{\Ccpp}{\Gls{C}\slash\gls{CPP}} + +\makeatletter +\newenvironment{compilationscheme} + {\allowdisplaybreaks\startalign} + {\endalign} +\makeatother diff --git a/self.bib b/self.bib index b12fade..a662e88 100644 --- a/self.bib +++ b/self.bib @@ -4,6 +4,7 @@ series = {{IFL} '19}, title = {Interpreting {Task} {Oriented} {Programs} on {Tiny} {Computers}}, isbn = {978-1-4503-7562-7}, + url = {https://doi.org/10.1145/3412932.3412936}, doi = {10.1145/3412932.3412936}, abstract = {Small Microcontroller Units (MCUs) drive the omnipresent Internet of Things (IoT). These devices are small, cheap, and energy efficient. However, they are not very powerful and lack an Operating System. Hence it is difficult to apply high level abstractions and write software that stays close to the design.Task Oriented Programming (TOP) is a paradigm for creating multi-user collaborative systems. A program consists of tasks—descriptions of what needs to be done. The tasks represent the actual work and a task value is observable during execution. Furthermore, tasks can be combined and transformed using combinators.mTask is an embedded Domain Specific Language (eDSL) to program MCUs following the TOP paradigm. Previous work has described the mTask language, a static C code generator, and how to integrate mTask with TOP servers. This paper shows that for dynamic IOT applications, tasks must be sent at runtime to the devices for interpretation. It describes in detail how to compile specialized IOT TOP tasks to bytecode and how to interpret them on devices with very little memory. These additions allow the creation of complete, dynamic IOT applications arising from a single source using a mix of iTasks and mTask tasks. Details such as serialization and communication are captured in simple abstractions.}, booktitle = {Proceedings of the 31st {Symposium} on {Implementation} and {Application} of {Functional} {Languages}}, @@ -27,7 +28,7 @@ year = {2019}, note = {in-press}, pages = {51}, - file = {Lubbers - Writing Internet of Things applications with Task .pdf:/home/mrl/.local/share/zotero/storage/ILZIBYW5/Lubbers - Writing Internet of Things applications with Task .pdf:application/pdf}, + file = {cefp.pdf:/home/mrl/.local/share/zotero/storage/VEWFI5DG/cefp.pdf:application/pdf}, } @inproceedings{lubbers_tiered_2020, @@ -35,6 +36,7 @@ series = {{IoT} '20}, title = {Tiered versus {Tierless} {IoT} {Stacks}: {Comparing} {Smart} {Campus} {Software} {Architectures}}, isbn = {978-1-4503-8758-3}, + url = {https://doi.org/10.1145/3410992.3411002}, doi = {10.1145/3410992.3411002}, abstract = {Internet of Things (IoT) software stacks are notoriously complex, conventionally comprising multiple tiers/components and requiring that the developer not only uses multiple programming languages, but also correctly interoperate the components. A novel alternative is to use a single tierless language with a compiler that generates the code for each component, and for their correct interoperation.We report the first ever systematic comparison of tiered and tierless IoT software architectures. The comparison is based on two implementations of a non-trivial smart campus application. PRSS has a conventional tiered Python-based architecture, and Clean Wemos Super Sensors (CWSS) has a novel tierless architecture based on Clean and the iTask and mTask embedded DSLs. An operational comparison of CWSS and PRSS demonstrates that they have equivalent functionality, and that both meet the University of Glasgow (UoG) smart campus requirements.Crucially, the tierless CWSS stack requires 70\% less code than the tiered PRSS stack. We analyse the impact of the following three main factors. (1) Tierless developers need to manage less interoperation: CWSS uses two DSLs in a single paradigm where PRSS uses five languages and three paradigms. (2) Tierless developers benefit from automatically generated, and hence correct, communication. (3) Tierless developers can exploit the powerful high-level abstractions such as Task Oriented Programming (TOP) in CWSS. A far smaller and single paradigm codebase improves software quality, dramatically reduces development time, and improves the maintainability of tierless stacks.}, booktitle = {Proceedings of the 10th {International} {Conference} on the {Internet} of {Things}}, @@ -51,6 +53,7 @@ title = {A {Task}-{Based} {DSL} for {Microcomputers}}, copyright = {All rights reserved}, isbn = {978-1-4503-6355-6}, + url = {http://dl.acm.org/citation.cfm?doid=3183895.3183902}, doi = {10.1145/3183895.3183902}, abstract = {The Internet of Things, IoT, makes small connected computing devices almost omnipresent. These devices have typically very limited computing power and severe memory restrictions to make them cheap and power efficient. These devices can interact with the environment via special sensors and actuators. Since each device controls several peripherals running interleaved, the control software is quite complicated and hard to maintain. Task Oriented Programming, TOP, offers lightweight communicating threads that can inspect each other’s intermediate results. This makes it well suited for the IoT. In this paper presents a functional task-based domain specific language for these IoT devices. We show that it yields concise control programs. By restricting the datatypes and using strict evaluation these programs fit within the restrictions of microcontrollers.}, language = {en}, @@ -141,6 +144,7 @@ @misc{koopman_sustrainable_2022, title = {{SusTrainable}: {Promoting} {Sustainability} as a {Fundamental} {Driver} in {Software} {Development} {Training} and {Education}. {Teacher} {Training}, {November} 1-5, {Nijmegen}, {The} {Netherlands}. {Revised} lecture notes}, copyright = {Creative Commons Attribution Non Commercial No Derivatives 4.0 International}, + url = {https://arxiv.org/abs/2204.13993}, publisher = {arXiv}, author = {Koopman, Pieter and Lubbers, Mart and Fernandes, João Paulo}, year = {2022}, @@ -183,19 +187,15 @@ pages = {20}, } -@article{lubbers_could_2022, - title = {Could {Tierless} {Languages} {Reduce} {IoT} {Development} {Grief}?}, - volume = {3}, - number = {5}, - journal = {ACM Trans. Internet Things}, - author = {Lubbers, Mart and Koopman, Pieter and Ramsingh, Adrian and Singer, Jeremy and Trinder, Phil}, - month = sep, - year = {2022}, - note = {Place: New York, NY, USA -Publisher: Association for Computing Machinery -in-press}, - keywords = {access control, internet-of-things, policy language, privilege escalation, Smart home system}, +@book{lubbers_orchestrating_2023, + address = {Nijmegen}, + title = {Orchestrating the {Internet} of {Things} using {Task}-{Oriented} {Programming}}, + shorttitle = {A {Cocktail} of {Tools}: {DSLs} for {TOSD}}, + publisher = {UB Nijmegen}, + author = {Lubbers, Mart}, + year = {2023}, } + @incollection{lubbers_green_2022, address = {Cham}, title = {Green {Computing} for the {Internet} of {Things}}, @@ -207,3 +207,30 @@ in-press}, note = {in-press}, pages = {1}, } + +@article{lubbers_could_2022, + title = {Could {Tierless} {Languages} {Reduce} {IoT} {Development} {Grief}?}, + issn = {2691-1914}, + url = {https://doi.org/10.1145/3572901}, + doi = {10.1145/3572901}, + abstract = {Internet of Things (IoT) software is notoriously complex, conventionally comprising multiple tiers. Traditionally an IoT developer must use multiple programming languages and ensure that the components interoperate correctly. A novel alternative is to use a single tierless language with a compiler that generates the code for each component and ensures their correct interoperation. We report a systematic comparative evaluation of two tierless language technologies for IoT stacks: one for resource-rich sensor nodes (Clean with iTask), and one for resource-constrained sensor nodes (Clean with iTask and mTask). The evaluation is based on four implementations of a typical smart campus application: two tierless and two Python-based tiered. (1) We show that tierless languages have the potential to significantly reduce the development effort for IoT systems, requiring 70\% less code than the tiered implementations. Careful analysis attributes this code reduction to reduced interoperation (e.g. two embedded domain-specific languages (DSLs) and one paradigm versus seven languages and two paradigms), automatically generated distributed communication, and powerful IoT programming abstractions. (2) We show that tierless languages have the potential to significantly improve the reliability of IoT systems, describing how Clean iTask/mTask maintains type safety, provides higher order failure management, and simplifies maintainability. (3) We report the first comparison of a tierless IoT codebase for resource-rich sensor nodes with one for resource-constrained sensor nodes. The comparison shows that they have similar code size (within 7\%), and functional structure. (4) We present the first comparison of two tierless IoT languages, one for resource-rich sensor nodes, and the other for resource-constrained sensor nodes.}, + journal = {ACM Trans. Internet Things}, + author = {Lubbers, Mart and Koopman, Pieter and Ramsingh, Adrian and Singer, Jeremy and Trinder, Phil}, + month = nov, + year = {2022}, + note = {Place: New York, NY, USA +Publisher: Association for Computing Machinery}, + keywords = {access control, internet-of-things, IoT stacks, policy language, privilege escalation, Smart home system, Tierless languages}, +} + +@inproceedings{koopman_strongly-typed_2022, + address = {New York, NY, USA}, + series = {{IFL} '22}, + title = {Strongly-{Typed} {Multi}-{View} {Stack}-{Based} {Computations}}, + booktitle = {Proceedings of the 34st {Symposium} on {Implementation} and {Application} of {Functional} {Languages}}, + publisher = {Association for Computing Machinery}, + author = {Koopman, Pieter and Lubbers, Mart}, + year = {2022}, + note = {event-place: Kopenhagen, Denmark. under-review}, + keywords = {clean, distributed applications, functional programming, internet of things, task oriented programming}, +} diff --git a/subfilepostamble.tex b/subfilepostamble.tex index 9e2e61b..b79519a 100644 --- a/subfilepostamble.tex +++ b/subfilepostamble.tex @@ -1,7 +1,9 @@ \ifSubfilesClassLoaded{% \bibliography{../other,../self,../tiot} + \begingroup% + \let\cleardoublepage\clearpage \printglossary[style=mcolindex]% \printglossary[type=\acronymtype,style=mcolindex]% -}{ - + \endgroup +}{% } diff --git a/subfilepreamble.tex b/subfilepreamble.tex index 07b0026..9a479c4 100644 --- a/subfilepreamble.tex +++ b/subfilepreamble.tex @@ -1,5 +1,6 @@ \ifSubfilesClassLoaded{% \pagenumbering{arabic} \externaldocument{\subfix{thesis}} + \let\cleardoublepage=\clearpage }{% } diff --git a/subfileprefix.tex b/subfileprefix.tex index 536397b..b915d30 100644 --- a/subfileprefix.tex +++ b/subfileprefix.tex @@ -1 +1,5 @@ -\ifSubfilesClassLoaded{\tableofcontents}{} +\ifSubfilesClassLoaded{% + \setcounter{tocdepth}{1} + \tableofcontents% +}{% +} diff --git a/top/4iot.tex b/top/4iot.tex index 10457ba..039af94 100644 --- a/top/4iot.tex +++ b/top/4iot.tex @@ -4,7 +4,6 @@ \begin{document} \input{subfileprefix} - \chapter{\texorpdfstring{\Glsxtrlong{TOP} for the \glsxtrlong{IOT}}{Task-oriented programming for the internet of things}}% \label{chp:top4iot} \begin{chapterabstract} @@ -46,7 +45,7 @@ The programs are usually cyclic executives instead of tasks running in an operat Different models of microcontrollers require their own vendor-provided drivers, hardware abstraction layer, compilers and \glspl{RTS}. There are many platforms that abstract away from this such as \gls{MBED} and \gls{ARDUINO} of which \gls{ARDUINO} is specifically designed for education and prototyping and hence used here. -The popular \gls{ARDUINO} \gls{C}\slash\gls{CPP} dialect and accompanying libraries provide an abstraction layer for common microcontroller behaviour allowing the programmer to program multiple types of microcontrollers using a single language. +The popular \gls{ARDUINO} \ccpp{} dialect and accompanying libraries provide an abstraction layer for common microcontroller behaviour allowing the programmer to program multiple types of microcontrollers using a single language. Originally it was designed for the in-house developed open-source hardware with the same name but the setup allows porting to many architectures. It provides an \gls{IDE} and toolchain automation to perform all steps of the toolchain with a single command. @@ -66,7 +65,7 @@ On microcontrollers, there usually is no screen for displaying text. Nevertheless, almost always there is a built-in monochrome $1\times1$ pixel screen, namely \pgls{LED}. The \emph{Hello World!} equivalent on microcontrollers blinks this \gls{LED}. -\Cref{lst:arduinoBlink} shows how the logic of a blink program might look when using \gls{ARDUINO}'s \gls{C}\slash\gls{CPP} dialect. +\Cref{lst:arduinoBlink} shows how the logic of a blink program might look when using \gls{ARDUINO}'s \ccpp{} dialect. Every \gls{ARDUINO} program contains a \arduinoinline{setup} and a \arduinoinline{loop} function. The \arduinoinline{setup} function is executed only once on boot, the \arduinoinline{loop} function is continuously called afterwards and contains the event loop. After setting the \gls{GPIO} pin to the correct mode, blink's \arduinoinline{loop} function alternates the state of the pin representing the \gls{LED} between \arduinoinline{HIGH} and \arduinoinline{LOW}, turning the \gls{LED} off and on respectively. @@ -201,7 +200,7 @@ With the \gls{MTASK} system, a \gls{TOP} programming language for \gls{IOT} edge The development of \gls{MTASK} or its predecessors has been going on for almost seven years now though it really set off during my master's thesis. This section provides an exhaustive overview of the work on \gls{MTASK} and its predecessors. -\subsection*{Generating \texorpdfstring{\gls{C}/\gls{CPP}}{C/C++} code} +\subsection*{Generating \texorpdfstring{\ccpp{}}{\ccpp{}} code} A first throw at a class-based shallowly \gls{EDSL} for microcontrollers was made by \citet{plasmeijer_shallow_2016}. The language was called \gls{ARDSL} and offered a type safe interface to \gls{ARDUINO} \gls{CPP} dialect. A \gls{CPP} code generation backend was available together with an \gls{ITASK} simulation backend. @@ -216,7 +215,7 @@ In this way, entire \gls{IOT} systems could be programmed from a single source. However, this version used a simplified version of \gls{MTASK} without functions. This was later improved upon by creating a simplified interface where \glspl{SDS} from \gls{ITASK} could be used in \gls{MTASK} and the other way around \citep{lubbers_task_2018}. It was shown by \citet{amazonas_cabral_de_andrade_developing_2018} that it was possible to build real-life \gls{IOT} systems with this integration. -Moreover, a course on the \gls{MTASK} simulator was provided at the 2018 \gls{CEFP}/\gls{3COWS} winter school in Ko\v{s}ice, Slovakia \citep{koopman_simulation_2018}. +Moreover, a course on the \gls{MTASK} simulator was provided at the 2018 \gls{CEFP}\slash\gls{3COWS} winter school in Ko\v{s}ice, Slovakia \citep{koopman_simulation_2018}. \subsection*{Transition to \texorpdfstring{\gls{TOP}}{TOP}} The \gls{MTASK} language as it is now was introduced in 2018 \citep{koopman_task-based_2018}. @@ -224,7 +223,7 @@ This paper updated the language to support functions, simple tasks, and \glspl{S Later the byte code compiler and \gls{ITASK} integration was added to the language \citep{lubbers_interpreting_2019}. Moreover, it was shown that it is very intuitive to write microcontroller applications in a \gls{TOP} language \citep{lubbers_multitasking_2019}. One reason for this is that a lot of design patterns that are difficult using standard means are for free in \gls{TOP} (e.g.\ multithreading). -In 2019, the \gls{CEFP} summer school in Budapest, Hungary hosted a course on developing \gls{IOT} applications with \gls{MTASK} as well \citep{lubbers_writing_2019}. +In 2019, the \gls{CEFP}\slash\gls{3COWS} summer school in Budapest, Hungary hosted a course on developing \gls{IOT} applications with \gls{MTASK} as well \citep{lubbers_writing_2019}. \subsection*{\texorpdfstring{\Glsxtrshort{TOP}}{TOP}} In 2022, the SusTrainable summer school in Rijeka, Croatia hosted a course on developing greener \gls{IOT} applications using \gls{MTASK} as well \citep{lubbers_green_2022}. diff --git a/top/green.tex b/top/green.tex index 4fb209a..5064d7e 100644 --- a/top/green.tex +++ b/top/green.tex @@ -4,7 +4,6 @@ \begin{document} \input{subfileprefix} - \chapter{Green computing with \texorpdfstring{\gls{MTASK}}{mTask}}% \label{chp:green_computing_mtask} \begin{chapterabstract} diff --git a/top/imp.tex b/top/imp.tex index a8f451c..1804c8e 100644 --- a/top/imp.tex +++ b/top/imp.tex @@ -4,7 +4,6 @@ \begin{document} \input{subfileprefix} - \chapter{Implementation}% \label{chp:implementation} \begin{chapterabstract} @@ -12,10 +11,574 @@ It is threefold: first it shows the implementation of the byte code compiler for \gls{MTASK}'s \gls{TOP} language, then is details of the implementation of \gls{MTASK}'s \gls{TOP} engine that executes the \gls{MTASK} tasks on the microcontroller, and finally it shows how the integration of \gls{MTASK} tasks and \glspl{SDS} is implemented both on the server and on the device. \end{chapterabstract} -\section{Byte code compiler} -IFL19 paper, bytecode instructieset~\cref{chp:bytecode_instruction_set} +Microcontrollers usually have flash-based program memory which wears out fairly quick. +For example, the atmega328p in the \gls{ARDUINO} UNO is rated for 10000 write cycles. +While this sounds like a lot, if new tasks are sent to the device every minute or so, a lifetime of not even seven days is guaranteed. +Hence, for dynamic applications, generating code at run-time for interpretation on the device is necessary. +This byte code is then interpreted on MCUs with very little memory and processing power and thus save precious write cycles of the program memory. +precious write cycles of the program memory. + +In order to provide the device with the tools to interpret the byte code, it is programmed with a \gls{RTS}, a customisable domain-specific \gls{OS} that takes care of the execution of tasks but also low-level mechanisms such as the communication, multi tasking, and memory management. +Once the device is programmed with the \gls{MTASK} \gls{RTS}, it can continuously receive new tasks. + +\subsection{Instruction set} +The instruction set is a fairly standard stack machine instruction set extended with special \gls{TOP} instructions. +\Cref{lst:instruction_type} shows the \gls{CLEAN} type representing the instruction set of which \cref{tbl:instr_task} gives detailed semantics. +Type synonyms are used to provide insight on the arguments of the instructions. +One notable instruction is the \cleaninline{MkTask} instruction, it allocates and initialises a task tree node and pushes a pointer to it on the stack. + +\begin{lstClean}[caption={The type housing the instruction set.},label={lst:instruction_type}] +:: ArgWidth :== UInt8 :: ReturnWidth :== UInt8 +:: Depth :== UInt8 :: Num :== UInt8 +:: SdsId :== UInt8 :: JumpLabel =: JL UInt16 + +//** Datatype housing all instructions +:: BCInstr + //Return instructions + //Jumps + = BCJumpF JumpLabel | BCJump JumpLabel | BCLabel JumpLabel | BCJumpSR ArgWidth JumpLabel + | BCReturn ReturnWidth ArgWidth | BCTailcall ArgWidth ArgWidth JumpLabel + //Arguments + | BCArgs ArgWidth ArgWidth + //Task node creation and refinement + | BCMkTask BCTaskType | BCTuneRateMs | BCTuneRateSec + //Task value ops + | BCIsStable | BCIsUnstable | BCIsNoValue | BCIsValue + //Stack ops + | BCPush String255 | BCPop Num | BCRot Depth Num | BCDup | BCPushPtrs + //Casting + | BCItoR | BCItoL | BCRtoI | ... + // arith + | BCAddI | BCSubI | ... + ... + +//** Datatype housing all task types +:: BCTaskType + = BCStableNode ArgWidth | ArgWidth + // Pin io + | BCReadD | BCWriteD | BCReadA | BCWriteA | BCPinMode + // Interrupts + | BCInterrupt + // Repeat + | BCRepeat + // Delay + | BCDelay | BCDelayUntil //* Only for internal use + // Parallel + | BCTAnd | BCTOr + //Step + | BCStep ArgWidth JumpLabel + //Sds ops + | BCSdsGet SdsId | BCSdsSet SdsId | BCSdsUpd SdsId JumpLabel + // Rate limiter + | BCRateLimit + ////Peripherals + //DHT + | BCDHTTemp UInt8 | BCDHTHumid UInt8 + ... +\end{lstClean} + +\subsection{Compiler} +The bytecode compiler interpretation for the \gls{MTASK} language is implemented as a monad stack containing a writer monad and a state monad. +The writer monad is used to generate code snippets locally without having to store them in the monadic values. +The state monad accumulates the code, and stores the stateful data the compiler requires. +\Cref{lst:compiler_state} shows the data type for the state, storing: +function the compiler currently is in; +code of the main expression; +context (see \todo{insert ref to compilation rules step here}); +code for the functions; +next fresh label; +a list of all the used \glspl{SDS}, either local \glspl{SDS} containing the initial value (\cleaninline{Left}) or lifted \glspl{SDS} (see \cref{sec:liftsds}) containing a reference to the associated \gls{ITASK} \gls{SDS}; +and finally there is a list of peripherals used. + +\begin{lstClean}[label={lst:compiler_state},caption={\Gls{MTASK}'s byte code compiler type}] +:: BCInterpret a :== StateT BCState (WriterT [BCInstr] Identity) a +:: BCState = + { bcs_infun :: JumpLabel + , bcs_mainexpr :: [BCInstr] + , bcs_context :: [BCInstr] + , bcs_functions :: Map JumpLabel BCFunction + , bcs_freshlabel :: JumpLabel + , bcs_sdses :: [Either String255 MTLens] + , bcs_hardware :: [BCPeripheral] + } +:: BCFunction = + { bcf_instructions :: [BCInstr] + , bcf_argwidth :: UInt8 + , bcf_returnwidth :: UInt8 + } +\end{lstClean} + +Executing the compiler is done by providing an initial state. +After compilation, several post-processing steps are applied to make the code suitable for the microprocessor. +First, in all tail call \cleaninline{BCReturn}'s are replaced by \cleaninline{BCTailCall} to implement tail call elimination. +Furthermore, all byte code is concatenated, resulting in one big program. +Many instructions have commonly used arguments so shorthands are introduced to reduce the program size. +For example, the \cleaninline{BCArg} instruction is often called with argument \qtyrange{0}{2} and can be replaced by the \cleaninline{BCArg0}--\cleaninline{BCArg2} shorthands. +Furthermore, redundant instructions (e.g.\ pop directly after push) are removed as well in order not to burden the code generation with these intricacies. +Finally the labels are resolved to represent actual program addresses instead of freshly generated identifiers. +After the byte code is ready, the lifted \glspl{SDS} are resolved to provide an initial value for them. +The result---byte code, \gls{SDS} specification and perpipheral specifications---are the result of the process, ready to be sent to the device. + +\section{Compilation rules} +This section describes the compilation rules, the translation from abstract syntax to byte code. +The compilation scheme consists of three schemes\slash{}functions. +When something is surrounded by $\parallel$, e.g.\ $\parallel{}a_i\parallel{}$, it denotes the number of stack cells required to store it. + +Some schemes have a \emph{context} $r$ as an argument which contains information about the location of the arguments in scope. +More information is given in the schemes requiring such arguments. + +\newcommand{\cschemeE}[2]{\mathcal{E}\llbracket#1\rrbracket~#2} +\newcommand{\cschemeF}[1]{\mathcal{F}\llbracket#1\rrbracket} +\newcommand{\cschemeS}[3]{\mathcal{S}\llbracket#1\rrbracket~#2~#3} +\begin{table} + \centering + \begin{tabularx}{\linewidth}{l X} + \toprule + Scheme & Description\\ + \midrule + $\cschemeE{e}{r}$ & Produces the value of expression $e$ given the context $r$ and pushes it on the stack. + The result can be a basic value or a pointer to a task.\\ + $\cschemeF{e}$ & Generates the bytecode for functions.\\ + $\cschemeS{e}{r}{w} $ & Generates the function for the step continuation given the context $r$ and the width $w$ of the left-hand side task value.\\ + \bottomrule + \end{tabularx} +\end{table} + +\subsection{Expressions} +Almost all expression constructions are compiled using $\mathcal{E}$. +The argument of $\mathcal{E}$ is the context (see \cref{ssec:functions}). +Values are always placed on the stack; tuples and other compound data types are unpacked. +Function calls, function arguments and tasks are also compiled using $\mathcal{E}$ but their compilations is explained later. + +\begin{align*} + \cschemeE{\text{\cleaninline{lit}}~e}{r} & = \text{\cleaninline{BCPush (bytecode e)}};\\ + \cschemeE{e_1\mathbin{\text{\cleaninline{+.}}}e_2}{r} & = \cschemeE{e_1}{r}; + \cschemeE{e_2}{r}; + \text{\cleaninline{BCAdd}};\\ + {} & \text{\emph{Similar for other binary operators}}\\ + \cschemeE{\text{\cleaninline{Not}}~e}{r} & = + \cschemeE{e}{r}; + \text{\cleaninline{BCNot}};\\ + {} & \text{\emph{Similar for other unary operators}}\\ + \cschemeE{\text{\cleaninline{If}}~e_1~e_2~e_3}{r} & = + \cschemeE{e_1}{r}; + \text{\cleaninline{BCJmpF}}\enskip l_{else}; \mathbin{\phantom{=}} \cschemeE{e_2}{r}; \text{\cleaninline{BCJmp}}\enskip l_{endif};\\ + {} & \mathbin{\phantom{=}} \text{\cleaninline{BCLabel}}\enskip l_{else}; \cschemeE{e_3}{r}; \mathbin{\phantom{=}} \text{\cleaninline{BCLabel}}\enskip l_{endif};\\ + {} & \text{\emph{Where $l_{else}$ and $l_{endif}$ are fresh labels}}\\ + \cschemeE{\text{\cleaninline{tupl}}~e_1~e_2}{r} & = + \cschemeE{e_1}{r}; + \cschemeE{e_2}{r};\\ + {} & \text{\emph{Similar for other unboxed compound data types}}\\ + \cschemeE{\text{\cleaninline{first}}~e}{r} & = + \cschemeE{e}{r}; + \text{\cleaninline{BCPop}}\enskip w;\\ + {} & \text{\emph{Where $w$ is the width of the left value and}}\\ + {} & \text{\emph{similar for other unboxed compound data types}}\\ + \cschemeE{\text{\cleaninline{second}}\enskip e}{r} & = + \cschemeE{e}{r}; + \text{\cleaninline{BCRot}}\enskip w_1\enskip (w_1+w_2); + \text{\cleaninline{BCPop}}\enskip w_2;\\ + {} & \text{\emph{Where $w_1$ is the width of the left and, $w_2$ of the right value}}\\ + {} & \text{\emph{similar for other unboxed compound data types}}\\ +\end{align*} + +Translating $\mathcal{E}$ to \gls{CLEAN} code is very straightforward, it basically means executing the monad. +Almost always, the type of the interpretation is not used, i.e.\ it is a phantom type. +To still have the functions return the correct type, the \cleaninline{tell`}\footnote{\cleaninline{tell` :: BCInterpret a}} helper is used. +This function is similar to the writer monad's \cleaninline{tell} function but is casted to the correct type. +\Cref{lst:imp_arith} shows the implementation for the arithmetic and conditional expressions. +Note that $r$, the context, is not an explicit argument but stored in the state. + +\begin{lstClean}[caption={Interpretation implementation for the arithmetic and conditional classes.},label={lst:imp_arith}] +instance expr BCInterpret where + lit t = tell` [BCPush (toByteCode{|*|} t)] + (+.) a b = a >>| b >>| tell` [BCAdd] + ... + If c t e = freshlabel >>= \elselabel->freshlabel >>= \endiflabel-> + c >>| tell` [BCJumpF elselabel] >>| + t >>| tell` [BCJump endiflabel,BCLabel elselabel] >>| + e >>| tell` [BCLabel endiflabel] +\end{lstClean} + +\subsection{Functions} +Compiling functions occurs in $\mathcal{F}$, which generates bytecode for the complete program by iterating over the functions and ending with the main expression. +When compiling the body of the function, the arguments of the function are added to the context so that the addresses can be determined when referencing arguments. +The main expression is a special case of $\mathcal{F}$ since it neither has arguments nor something to continue. +Therefore, it is just compiled using $\mathcal{E}$. -\section{Run time system} +\begin{align*} + \cschemeF{main=m} & = + \cschemeE{m}{[]};\\ + \cschemeF{f~a_0 \ldots a_n = b~\text{\cleaninline{In}}~m} & = + \text{\cleaninline{BCLabel}}~f;\\ + {} & \mathbin{\phantom{=}} \cschemeE{b}{[\langle f, i\rangle, i\in \{(\Sigma^n_{i=0}\parallel{}a_i\parallel{})..0\}]};\\ + {} & \mathbin{\phantom{=}} \text{\cleaninline{BCReturn}}~\parallel{}b\parallel{}~n;\\ + {} & \mathbin{\phantom{=}} \cschemeF{m};\\ +\end{align*} +% +%A function call starts by pushing the stack and frame pointer, and making space for the program counter (a) followed by evaluating the arguments in reverse order (b). +%On executing \Cl{BCJumpSR}, the program counter is set and the interpreter jumps to the function (c). +%When the function returns, the return value overwrites the old pointers and the arguments. +%This occurs right after a \Cl{BCReturn} (d). +%Putting the arguments on top of pointers and not reserving space for the return value uses little space and facilitates tail call optimization. +% +%\begin{figure} +% \subfigure[\Cl{BCPushPtrs}]{\includegraphics[width=.24\linewidth]{memory1}} +% \subfigure[Arguments]{\includegraphics[width=.24\linewidth]{memory2}} +% \subfigure[\Cl{BCJumpSR}]{\includegraphics[width=.24\linewidth]{memory3}} +% \subfigure[\Cl{BCReturn}]{\includegraphics[width=.24\linewidth]{memory4}} +% \caption{The stack layout during function calls.} +% \Description{A visual representation of the stack layout during a function call and a return.} +%\end{figure} +% +%Calling a function and referencing function arguments are an extension to $\mathcal{E}$ as shown below. +%Arguments may be at different places on the stack at different times (see Subsection~\ref{ssec:step}) and therefore the exact location always has to be determined from the context using \Cl{findarg}\footnote{% +% \lstinline{findarg [l':r] l = if (l == l`) 0 (1 + findarg r l)} +%}. +%Compiling argument $a_{f^i}$, the $i$th argument in function $f$, consists of traversing all positions in the current context. +%Arguments wider than one stack cell are fetched in reverse to preserve the order. +% +%\begin{compilationscheme} +% \cschemeE{f(a_0, \ldots, a_n)}{r} & = +% \text{\Cl{BCPushPtrs}};\\ +% {} & \mathbin{\phantom{=}} \cschemeE{a_n}{r}; \cschemeE{a_{\ldots}}{r}; \cschemeE{a_0}{r};\\ +% {} & \mathbin{\phantom{=}} \text{\Cl{BCJumpSR}}\enskip n\enskip f;\\ +% \cschemeE{a_{f^i}}{r} & = +% \text{\Cl{BCArg} findarg}(r, f, i)\enskip \text{for all}\enskip i\in\{w\ldots v\};\\ +% {} & v = \Sigma^{i-1}_{j=0}\|a_{f^j}\|\\ +% {} & w = v + \|a_{f^i}\|\\ +%\end{compilationscheme} +% +%Translating the compilation schemes for functions to Clean is not as straightforward as other schemes due to the nature of shallow embedding. +%The \Cl{fun} class has a single function with a single argument. +%This argument is a Clean function that---when given a callable Clean function representing the mTask function---will produce \Cl{main} and a callable function. +%To compile this, the argument must be called with a function representing a function call in mTask. +%Listing~\ref{lst:fun_imp} shows the implementation for this as Clean code. +%To uniquely identify the function, a fresh label is generated. +%The function is then called with the \Cl{callFunction} helper function that generates the instructions that correspond to calling the function. +%That is, it pushes the pointers, compiles the arguments, and writes the \Cl{JumpSR} instruction. +%The resulting structure (\Cl{g In m}) contains a function representing the mTask function (\Cl{g}) and the \Cl{main} structure to continue with. +%To get the actual function, \Cl{g} must be called with representations for the argument, i.e.\ using \Cl{findarg} for all arguments. +%The arguments are added to the context and \Cl{liftFunction} is called with the label, the argument width and the compiler. +%This function executes the compiler, decorates the instructions with a label and places them in the function dictionary together with the metadata such as the argument width. +%After lifting the function, the context is cleared again and compilation continues with the rest of the program. +% +%\begin{lstlisting}[language=Clean,label={lst:fun_imp},caption={The backend implementation for functions.}] +%instance fun (BCInterpret a) BCInterpret | type a where +% fun def = {main=freshlabel >>= \funlabel-> +% let (g In m) = def \a->callFunction funlabel (byteWidth a) [a] +% in addToCtx funlabel zero (argwidth def) +% >>| liftFunction funlabel (argwidth def) +% (g (findArgs funlabel zero (argwidth def))) Nothing +% >>| clearCtx >>| m.main +% } +% +%callFunction :: JumpLabel UInt8 [BCInterpret b] -> BCInterpret c | ... +%liftFunction :: JumpLabel UInt8 (BCInterpret a) (Maybe UInt8) -> BCInterpret () +%\end{lstlisting} +% +%\subsection{Tasks}\label{ssec:scheme_tasks} +%Task trees are created with the \Cl{BCMkTask} instruction that allocates a node and pushes it to the stack. +%It pops arguments from the stack according to the given task type. +%The following extension of $\mathcal{E}$ shows this compilation scheme (except for the step combinator, explained in Subsection~\ref{ssec:step}). +% +%\begin{compilationscheme} +% \cschemeE{\text{\Cl{rtrn}}\enskip e}{r} & = +% \cschemeE{e}{r}; +% \text{\Cl{BCMkTask BCStable}}_{\|e\|};\\ +% \cschemeE{\text{\Cl{unstable}}\enskip e}{r} & = +% \cschemeE{e}{r}; +% \text{\Cl{BCMkTask BCUnstable}}_{\|e\|};\\ +% \cschemeE{\text{\Cl{readA}}\enskip e}{r} & = +% \cschemeE{e}{r}; +% \text{\Cl{BCMkTask BCReadA}};\\ +% \cschemeE{\text{\Cl{writeA}}\enskip e_1\enskip e_2}{r} & = +% \cschemeE{e_1}{r}; +% \cschemeE{e_2}{r}; +% \text{\Cl{BCMkTask BCWriteA}};\\ +% \cschemeE{\text{\Cl{readD}}\enskip e}{r} & = +% \cschemeE{e}{r}; +% \text{\Cl{BCMkTask BCReadD}};\\ +% \cschemeE{\text{\Cl{writeD}}\enskip e_1\enskip e_2}{r} & = +% \cschemeE{e_1}{r}; +% \cschemeE{e_2}{r}; +% \text{\Cl{BCMkTask BCWriteD}};\\ +% \cschemeE{\text{\Cl{delay}}\enskip e}{r} & = +% \cschemeE{e}{r}; +% \text{\Cl{BCMkTask BCDelay}};\\ +% \cschemeE{\text{\Cl{rpeat}}\enskip e}{r} & = +% \cschemeE{e}{r}; +% \text{\Cl{BCMkTask BCRepeat}};\\ +% \cschemeE{e_1\text{\Cl{.||.}}e_2}{r} & = +% \cschemeE{e_1}{r}; +% \cschemeE{e_2}{r}; +% \text{\Cl{BCMkTask BCOr}};\\ +% \cschemeE{e_1\text{\Cl{.&&.}}e_2}{r} & = +% \cschemeE{e_1}{r}; +% \cschemeE{e_2}{r}; +% \text{\Cl{BCMkTask BCAnd}};\\ +%\end{compilationscheme} +% +%This simply translates to Clean code by writing the correct \Cl{BCMkTask} instruction as exemplified in Listing~\ref{lst:imp_ret}. +% +%\begin{lstlisting}[language=Clean,caption={The backend implementation for \Cl{rtrn}.},label={lst:imp_ret}] +%instance rtrn BCInterpret where rtrn m = m >>| tell` [BCMkTask (bcstable m)] +%\end{lstlisting} +% +%\subsection{Step combinator}\label{ssec:step} +%The \Cl{step} construct is a special type of task because the task value of the left-hand side may change over time. +%Therefore, the continuation tasks on the right-hand side are \emph{observing} this task value and acting upon it. +%In the compilation scheme, all continuations are first converted to a single function that has two arguments: the stability of the task and its value. +%This function either returns a pointer to a task tree or fails (denoted by $\bot$). +%It is special because in the generated function, the task value of a task can actually be inspected. +%Furthermore, it is a lazy node in the task tree: the right-hand side may yield a new task tree after several rewrite steps (i.e.\ it is allowed to create infinite task trees using step combinators). +%The function is generated using the $\mathcal{S}$ scheme that requires two arguments: the context $r$ and the width of the left-hand side so that it can determine the position of the stability which is added as an argument to the function. +%The resulting function is basically a list of if-then-else constructions to check all predicates one by one. +%Some optimization is possible here but has currently not been implemented. +% +%\begin{compilationscheme} +% \cschemeE{t_1\text{\Cl{>>*.}}t_2}{r} & = +% \cschemeE{a_{f^i}}{r}, \langle f, i\rangle\in r; +% \text{\Cl{BCMkTask}}\enskip \text{\Cl{BCStable}}_{\|r\|};\\ +% {} & \mathbin{\phantom{=}} \cschemeE{t_1}{r};\\ +% {} & \mathbin{\phantom{=}} \text{\Cl{BCMkTask}}\enskip \text{\Cl{BCAnd}};\\ +% {} & \mathbin{\phantom{=}} \text{\Cl{BCMkTask}}\\ +% {} & \mathbin{\phantom{=}} \enskip (\text{\Cl{BCStep}}\enskip (\cschemeS{t_2}{(r + [\langle l_s, i\rangle])}{\|t_1\|}));\\ +%% +% \cschemeS{[]}{r}{w} & = +% \text{\Cl{BCPush}}\enskip \bot;\\ +% \cschemeS{\text{\Cl{IfValue}}\enskip f\enskip t:cs}{r}{w} & = +% \text{\Cl{BCArg}} (\|r\| + w); +% \text{\Cl{BCIsNoValue}};\\ +% {} & \mathbin{\phantom{=}} \cschemeE{f}{r}; +% \text{\Cl{BCAnd}};\\ +% {} & \mathbin{\phantom{=}} \text{\Cl{BCJmpF}}\enskip l_1;\\ +% {} & \mathbin{\phantom{=}} \cschemeE{t}{r}; +% \text{\Cl{BCJmp}}\enskip l_2;\\ +% {} & \mathbin{\phantom{=}} \text{\Cl{BCLabel}}\enskip l_1; +% \cschemeS{cs}{r}{w};\\ +% {} & \mathbin{\phantom{=}} \text{\Cl{BCLabel}}\enskip l_2;\\ +% {} & \text{\emph{Where $l_1$ and $l_2$ are fresh labels}}\\ +% {} & \text{\emph{Similar for \Cl{IfStable} and \Cl{IfUnstable}}}\\ +% \cschemeS{\text{\Cl{IfNoValue}}\enskip t:cs}{r}{w} & = +% \text{\Cl{BCArg}} (\|r\|+w); +% \text{\Cl{BCIsNoValue}};\\ +% {} & \mathbin{\phantom{=}} \text{\Cl{BCJmpF}}\enskip l_1;\\ +% {} & \mathbin{\phantom{=}} \cschemeE{t}{r}; +% \text{\Cl{BCJmp}}\enskip l_2;\\ +% {} & \mathbin{\phantom{=}} \text{\Cl{BCLabel}}\enskip l_1; +% \cschemeS{cs}{r}{w};\\ +% {} & \mathbin{\phantom{=}} \text{\Cl{BCLabel}}\enskip l_2;\\ +% {} & \text{\emph{Where $l_1$ and $l_2$ are fresh labels}}\\ +% \cschemeS{\text{\Cl{Always}}\enskip f:cs}{r}{w} & = +% \cschemeE{f}{r};\\ +%\end{compilationscheme} +% +%First the context is evaluated. +%The context contains arguments from functions and steps that need to be preserved after rewriting. +%The evaluated context is combined with the left-hand side task value by means of a \Cl{.&&.} combinator to store it in the task tree so that it is available after a rewrite. +%This means that the task tree is be transformed as follows: +% +%\begin{lstlisting}[language=Clean] +%t1 >>= \v1->t2 >>= \v2->t3 >>= ... +%//is transformed to +%t1 >>= \v1->rtrn v1 .&&. t2 >>= \v2->rtrn (v1, v2) .&&. t3 >>= ... +%\end{lstlisting} +% +%The translation to Clean is given in Listing~\ref{lst:imp_seq}. +% +%\begin{lstlisting}[language=Clean,caption={Backend implementation for the step class.},label={lst:imp_seq}] +%instance step BCInterpret where +% (>>*.) lhs cont +% //Fetch a fresh label and fetch the context +% = freshlabel >>= \funlab->gets (\s->s.bcs_context) +% //Generate code for lhs +% >>= \ctx->lhs +% //Possibly add the context +% >>| tell` (if (ctx =: []) [] +% //The context is just the arguments up till now in reverse +% ( [BCArg (UInt8 i)\\i<-reverse (indexList ctx)] +% ++ map BCMkTask (bcstable (UInt8 (length ctx))) +% ++ [BCMkTask BCTAnd] +% )) +% //Increase the context +% >>| addToCtx funlab zero lhswidth +% //Lift the step function +% >>| liftFunction funlab +% //Width of the arguments is the width of the lhs plus the +% //stability plus the context +% (one + lhswidth + (UInt8 (length ctx))) +% //Body label ctx width continuations +% (contfun funlab (UInt8 (length ctx))) +% //Return width (always 1, a task pointer) +% (Just one) +% >>| modify (\s->{s & bcs_context=ctx}) +% >>| tell` [BCMkTask $ instr rhswidth funlab] +% +%toContFun :: JumpLabel UInt8 -> BCInterpret a +%toContFun steplabel contextwidth +% = foldr tcf (tell` [BCPush fail]) cont +%where +% tcf (IfStable f t) +% = If ((stability >>| tell` [BCIsStable]) &. f val) +% (t val >>| tell` []) +% ... +% stability = tell` [BCArg $ lhswidth + contextwidth] +% val = retrieveArgs steplabel zero lhswidth +%\end{lstlisting} +% +%\subsection{Shared Data Sources} +%The compilation scheme for SDS definitions is a trivial extension to $\mathcal{F}$ since there is no code generated as seen below. +% +%\begin{compilationscheme} +% \cschemeF{\text{\Cl{sds}}\enskip x=i\enskip \text{\Cl{In}}\enskip m} & = +% \cschemeF{m};\\ +% \cschemeF{\text{\Cl{liftsds}}\enskip x=i\enskip \text{\Cl{In}}\enskip m} & = +% \cschemeF{m};\\ +%\end{compilationscheme} +% +%The SDS access tasks have a compilation scheme similar to other tasks (see~Subsection~\ref{ssec:scheme_tasks}). +%The \Cl{getSds} task just pushes a task tree node with the SDS identifier embedded. +%The \Cl{setSds} task evaluates the value, lifts that value to a task tree node and creates an SDS set node. +% +%\begin{compilationscheme} +% \cschemeE{\text{\Cl{getSds}}\enskip s}{r} & = +% \text{\Cl{BCMkTask}} (\text{\Cl{BCSdsGet}} s);\\ +% \cschemeE{\text{\Cl{setSds}}\enskip s\enskip e}{r} & = +% \cschemeE{e}{r}; +% \text{\Cl{BCMkTask BCStable}}_{\|e\|};\\ +% {} & \mathbin{\phantom{=}} \text{\Cl{BCMkTask}} (\text{\Cl{BCSdsSet}} s);\\ +%\end{compilationscheme} +% +%While there is no code generated in the definition, the bytecode compiler is storing the SDS data in the \Cl{bcs_sdses} field in the compilation state. +%The SDSs are typed as functions in the host language so an argument for this function must be created that represents the SDS on evaluation. +%For this, an \Cl{BCInterpret} is created that emits this identifier. +%When passing it to the function, the initial value of the SDS is returned. +%This initial value is stored as a bytecode encoded value in the state and the compiler continues with the rest of the program. +% +%Compiling \Cl{getSds} is a matter of executing the \Cl{BCInterpret} representing the SDS, which yields the identifier that can be embedded in the instruction. +%Setting the SDS is similar: the identifier is retrieved and the value is written to put in a task tree so that the resulting task can remember the value it has written. +%Lifted SDSs are compiled in a very similar way. +%The only difference is that there is no initial value but an iTasks SDS when executing the Clean function. +%A lens on this SDS converting \Cl{a} from the \Cl{Shared a} to a \Cl{String255}---a bytecode encoded version---is stored in the state. +%The encoding and decoding itself is unsafe when used directly but the type system of the language and the abstractions make it safe. +%Upon sending the mTask task to the device, the initial values of the lifted SDSs are fetched to complete the SDS specification. +% +%\begin{lstlisting}[language=Clean,caption={Backend implementation for the SDS classes.},label={lst:comp_sds}] +%:: Sds a = Sds Int +%instance sds BCInterpret where +% sds def = {main = freshsds >>= \sdsi-> +% let sds = modify (\s->{s & bcs_sdses=put sdsi +% (Left (toByteCode t)) s.bcs_sdses}) +% >>| pure (Sds sdsi) +% (t In e) = def sds +% in e.main} +% getSds f = f >>= \(Sds i)-> tell` [BCMkTask (BCSdsGet (fromInt i))] +% setSds f v = f >>= \(Sds i)->v >>| tell` +% ( map BCMkTask (bcstable (byteWidth v)) +% ++ [BCMkTask (BCSdsSet (fromInt i))])\end{lstlisting} +% +%\section{Run time system} +% +%The RTS is designed to run on systems with as little as 2kB of RAM. +%Aggressive memory management is therefore vital. +%Not all firmwares for MCUs support heaps and---when they do---allocation often leaves holes when not used in a Last In First Out strategy. +%Therefore the RTS uses a chunk of memory in the global data segment with its own memory manager tailored to the needs of mTask. +%The size of this block can be changed in the configuration of the RTS if necessary. +%On an Arduino {UNO} ---equipped with 2kB of RAM--- this size can be about 1500 bytes. +% +%In memory, task data grows from the bottom up and an interpreter stack is located directly on top of it growing in the same direction. +%As a consequence, the stack moves when a new task is received. +%This never happens within execution because communication is always processed before execution. +%Values in the interpreter are always stored on the stack, even tuples. +%Task trees grow from the top down as in a heap. +%This approach allows for flexible ratios, i.e.\ many tasks and small trees or few tasks and big trees. +% +%The event loop of the RTS is executed repeatedly and consists of three distinct phases. +% +%%TODO evt subsubsections verwijderen +%\subsubsection{Communication} +%In the first phase, the communication channels are processed. +%The messages announcing SDS updates are applied immediately, the initialization of new tasks is delayed until the next phase. +% +%\subsubsection{Execution} +%The second phase consists of executing tasks. +%The RTS executes all tasks in a round robin fashion. +%If a task is not initialized yet, the bytecode of the main function is interpreted to produce the initial task tree. +%The rewriting engine uses the interpreter when needed, e.g.\ to calculate the step continuations. +%The rewriter and the interpreter use the same stack to store intermediate values. +%Rewriting steps are small so that interleaving results in seemingly parallel execution. +%In this phase new task tree nodes may be allocated. +%Both rewriting and initialization are atomic operations in the sense that no processing on SDSs is done other than SDS operations from the task itself. +%The host is notified if a task value is changed after a rewrite step. +% +%\subsubsection{Memory management} +%The third and final phase is memory management. +%Stable tasks, and unreachable task tree nodes are removed. +%If a task is to be removed, tasks with higher memory addresses are moved down. +%For task trees---stored in the heap---the RTS already marks tasks and task trees as trash during rewriting so the heap can be compacted in a single pass. +%This is possible because there is no sharing or cycles in task trees and nodes contain pointers pointers to their parent. +%\subsection{Memory management} +%\subsection{Interpreter} +%\subsection{Rewrite engine} +%\section{Task rewriting}\label{sec:rewrite} +%Tasks are rewritten every event loop iteration and one rewrite cycle is generally very fast. +%This results in seemingly parallel execution of the tasks because the rewrite steps are interleaved. +%Rewriting is a destructive process that actually modifies the task tree nodes in memory and marks nodes that become garbage. +%The task value is stored on the stack and therefore only available during rewriting. +% +%\subsection{Basic tasks} +%The \Cl{rtrn} and \Cl{unstable} tasks always rewrite to themselves and have no side effects. +%The GPIO interaction tasks do have side effects. +%The \Cl{readA} and \Cl{readD} tasks will query the given pin every rewrite cycle and emit it as an unstable task value. +%The \Cl{writeA} and \Cl{writeD} tasks write the given value to the given pin and immediately rewrite to a stable task of the written value. +% +%\subsection{Delay and repetition} +%The \Cl{delay} task stabilizes once a certain amount of time has been passed by storing the finish time on initialization. +%In every rewrite step it checks whether the current time is bigger than the finish time and if so, it rewrites to a \Cl{rtrn} task containing the number of milliseconds that it overshot the target. +%The \Cl{rpeat} task combinator rewrites the argument until it becomes stable. +%Rewriting is a destructive process and therefore the original task tree must be saved. +%As a consequence, on installation, the argument is cloned and the task rewrites the clone. +% +%\subsection{Sequential combination} +%First the left-hand side of the step task is rewritten. +%The resulting value is passed to the continuation function. +%If the continuation function returns a pointer to a task tree, the task tree rewrites to that task tree and marks the original left-hand side as trash. +%If the function returns $\bot$, the step is kept unchanged. +%The step itself never fields a value. +% +%\subsection{Parallel combination}\label{ssec:parallelExecution} +%There are two parallel task combinators available. +%A \Cl{.&&.} task only becomes stable when both sides are stable. +%A \Cl{.||.} task becomes stable when one of the sides is stable. +%The combinators first rewrite both sides and then merge the task values according to the semantics given in Listing~\ref{lst:parallel_combinators}. +% +%\begin{lstlisting}[language=Clean,caption={Task value semantics for the parallel combinators.},label={lst:parallel_combinators}] +%(.&&.) :: (TaskValue a) (TaskValue b) -> TaskValue (a, b) +%(.&&.) (Value lhs stab1) (Value rhs stab2) = Value (lhs, rhs) (stab1 && stab2) +%(.&&.) _ _ = NoValue +% +%(.||.) :: (TaskValue a) (TaskValue a) -> TaskValue a +%(.||.) lhs=:(Value _ True) _ = lhs +%(.||.) (Value lhs _) rhs=:(Value _ True) = rhs +%(.||.) NoValue rhs = rhs +%(.||.) lhs _ = lhs\end{lstlisting} +% +%\subsection{Shared Data Source tasks} +%The \Cl{BCSdsGet} node always rewrites to itself. +%It will read the actual SDS embedded and emit the value as an unstable task value. +% +%Setting an SDS is a bit more involved because after writing, it emits the value written as a stable task value. +%The \Cl{BCSdsSet} node contains the identifier for the SDS and a task tree that, when rewritten, emits the value to be set as a stable task value. +%The naive approach would be to just rewrite the \Cl{BCSdsSet} to a node similar to the \Cl{BCSdsGet} but only with a stable value. +%However, after writing the SDS, its value might have changed due to other tasks writing it, and then the \Cl{setSDS}'s stable value may change. +%Therefore, the \Cl{BCSdsSet} node is rewritten to the argument task tree which always represents constant stable value. +%In future rewrites, the constant value node emits the value that was originally written. +% +%The client only knows whether an SDS is a lifted SDS, not to which iTasks SDS it is connected. +%If the SDS is modified on the device, it sends an update to the server. +% +%\section{Conclusion} \input{subfilepostamble} \end{document} diff --git a/top/int.tex b/top/int.tex index 1b310cd..a0e0224 100644 --- a/top/int.tex +++ b/top/int.tex @@ -4,7 +4,6 @@ \begin{document} \input{subfileprefix} - \chapter{Integration with \texorpdfstring{\gls{ITASK}}{iTask}}% \label{chp:integration_with_itask} \begin{chapterabstract} @@ -27,7 +26,7 @@ The entire system is written as a single \gls{CLEAN} specification where multipl Tasks can access \glspl{SDS} according to many-to-many communication and multiple clients can work on the same task. Devices are integrated into the system using the \cleaninline{withDevice} function (see \cref{sec:withdevice}). Using \cleaninline{liftmTask}, \gls{MTASK} tasks are lifted to a device (see \cref{sec:liftmtask}). -\Gls{ITASK} \glspl{SDS} are lifted to the \gls{MTASK} device using \cleaninline{liftsds} (see \cref{sec:liftmtask}). +\Gls{ITASK} \glspl{SDS} are lifted to the \gls{MTASK} device using \cleaninline{liftsds} (see \cref{sec:liftsds}). \begin{figure}[ht] \centering @@ -158,7 +157,7 @@ class liftsds v where -> Main (MTask v u) | RWShared sds \end{lstClean} -As an example, \cref{lst:mtask_liftsds_ex} shows a lightswitch function producing an \gls{ITASK}\slash\gls{MTASK} task. +As an example, \cref{lst:mtask_liftsds_ex} shows a lightswitch function producing an \imtask{} task. Given an \cleaninline{MTDevice} type, a device handle, an \gls{ITASK} \gls{SDS} of the type boolean is created. This boolean represents the state of the light. The \gls{MTASK} task uses this \gls{SDS} to turn on or off the light. diff --git a/top/lang.tex b/top/lang.tex index a5916cd..f7e9ec3 100644 --- a/top/lang.tex +++ b/top/lang.tex @@ -4,7 +4,6 @@ \begin{document} \input{subfileprefix} - \chapter{The \texorpdfstring{\gls{MTASK}}{mTask} language}%\texorpdfstring{\glsxtrshort{DSL}}{DSL}}% \label{chp:mtask_dsl} \begin{chapterabstract} @@ -52,11 +51,11 @@ The class constraints for values in \gls{MTASK} are omnipresent in all functions \begin{table}[ht] \centering - \caption{Translation from \gls{CLEAN}/\gls{MTASK} data types to \gls{CPP} datatypes.}% + \caption{Translation from \gls{CLEAN}\slash\gls{MTASK} data types to \ccpp{} datatypes.}% \label{tbl:mtask-c-datatypes} \begin{tabular}{lll} \toprule - \gls{CLEAN}/\gls{MTASK} & \gls{CPP} type & \textnumero{}bits\\ + \gls{CLEAN}\slash\gls{MTASK} & \ccpp{} type & \textnumero{}bits\\ \midrule \cleaninline{Bool} & \cinline{bool} & 16\\ \cleaninline{Char} & \cinline{char} & 16\\ @@ -370,6 +369,7 @@ task2 = declarePin D3 PMOutput \d3->{main=writeD d3 true} \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. 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. diff --git a/tvt/tvt.tex b/tvt/tvt.tex index e6f3295..e861028 100644 --- a/tvt/tvt.tex +++ b/tvt/tvt.tex @@ -1,8 +1,9 @@ \documentclass[../thesis.tex]{subfiles} -\include{subfilepreamble} +\input{subfilepreamble} \begin{document} +\input{subfileprefix} \chapter{Could Tierless Languages Reduce IoT Development Grief?}% \label{chp:smart_campus} @@ -15,7 +16,7 @@ %, with two targeting microcontrollers and two targeting supersensors. \begin{enumerate*} \item We show that tierless languages have the potential to significantly reduce the development effort for \gls{IOT} systems, requiring 70\% less code than the tiered implementations. Careful analysis attributes this code reduction to reduced interoperation (e.g.\ two \glspl{EDSL} and one paradigm versus seven languages and two paradigms), automatically generated distributed communication, and powerful \gls{IOT} programming abstractions. - \item We show that tierless languages have the potential to significantly improve the reliability of \gls{IOT} systems, describing how \gls{CLEAN}\slash\gls{ITASK}/\gls{MTASK} maintains type safety, provides higher order failure management, and simplifies maintainability. + \item We show that tierless languages have the potential to significantly improve the reliability of \gls{IOT} systems, describing how \cimtask{} maintains type safety, provides higher order failure management, and simplifies maintainability. \item We report the first comparison of a tierless \gls{IOT} codebase for resource-rich sensor nodes with one for resource-constrained sensor nodes. The comparison shows that they have similar code size (within 7\%), and functional structure. \item We present the first comparison of two tierless \gls{IOT} languages, one for resource-rich sensor nodes, and the other for resource-constrained sensor nodes. \end{enumerate*} @@ -26,7 +27,7 @@ Conventional \gls{IOT} software stacks are notoriously complex and pose very significant software development, reliability, and maintenance challenges. \Gls{IOT} software architectures typically comprise multiple components organised in four or more tiers or layers \citep{sethi2017internet,Ravulavaru18,Alphonsa20}. This is due to the highly distributed nature of typical \gls{IOT} applications that must read sensor data from end points (the \emph{perception} layer), aggregate and select the data and communicate over a network (the \emph{network} layer), store the data in a database and analyse it (the \emph{application} layer) and display views of the data, commonly on web pages (the \emph{presentation} layer). -Conventional \gls{IOT} software architectures require the development of separate programs in various programming languages for each of the components/tiers in the stack. This is modular, but a significant burden for developers, and some key challenges are as follows. +Conventional \gls{IOT} software architectures require the development of separate programs in various programming languages for each of the components\slash{}tiers in the stack. This is modular, but a significant burden for developers, and some key challenges are as follows. \begin{enumerate*} \item Interoperating components in multiple languages and paradigms increases the developer's cognitive load who must simultaneously think in multiple languages and paradigms, i.e.\ manage significant semantic friction. \item The developer must correctly interoperate the components, e.g.\ adhere to the \gls{API} or communication protocols between components. @@ -34,9 +35,9 @@ Conventional \gls{IOT} software architectures require the development of separat \item The developer must deal with the potentially diverse failure modes of each component, and of component interoperations. \end{enumerate*} -A radical alternative development paradigm uses a single \emph{tierless} language that synthesizes all components/tiers in the software stack. There are established \emph{tierless} languages for web stacks, e.g.\ Links \citep{cooper2006links} or Hop \citep{serrano2006hop}. +A radical alternative development paradigm uses a single \emph{tierless} language that synthesizes all components\slash{}tiers in the software stack. There are established \emph{tierless} languages for web stacks, e.g.\ Links \citep{cooper2006links} or Hop \citep{serrano2006hop}. In a tierless language the developer writes the application as a single program. The code for different tiers is simultaneously checked by the compiler, and compiled to the required component languages. For example, Links compiles to HTML and JavaScript for the web client and to SQL on the server to interact with the database system. Tierless languages for \gls{IOT} stacks are more recent and less common, examples include -Potato \citep{troyer_building_2018} and \gls{CLEAN} with \gls{ITASK}\slash\gls{MTASK} \citep{lubbers_interpreting_2019}. +Potato \citep{troyer_building_2018} and \gls{CLEAN} with \imtask{} \citep{lubbers_interpreting_2019}. \Gls{IOT} sensor nodes may be microcontrollers with very limited compute resources, or supersensors: resource-rich single board computers like a Raspberry Pi. A tierless language may target either class of sensor node, and microcontrollers are the more demanding target due to the limited resources, e.g.\ small memory, executing on bare metal \etc. @@ -173,12 +174,12 @@ An example tierless web framework that uses a \gls{DSL} is Haste \citep{10.1145/ \subsection{Tierless \texorpdfstring{\glsxtrshort{IOT}}{IoT} languages} The use of tierless languages in \gls{IOT} applications is both more recent and less common than for web applications. Tierless \gls{IOT} programming may extend tierless web programming by adding network and perception layers. -The presentation layer of a tierless \gls{IOT} language, like tierless web languages, benefits from almost invariably executing in a standard browser. The perception layer faces greater challenges, often executing on one of a set of slow and resource-constrained microcontrollers. Hence, tierless \gls{IOT} languages typically compile the perception layer to either \gls{C}\slash\gls{CPP} (the lingua franca of microcontrollers), or to some intermediate representation to be interpreted. +The presentation layer of a tierless \gls{IOT} language, like tierless web languages, benefits from almost invariably executing in a standard browser. The perception layer faces greater challenges, often executing on one of a set of slow and resource-constrained microcontrollers. Hence, tierless \gls{IOT} languages typically compile the perception layer to either \ccpp{} (the lingua franca of microcontrollers), or to some intermediate representation to be interpreted. \subsubsection{\texorpdfstring{\Glsxtrlongpl{DSL}}{DSLs} for microcontrollers} Many \glspl{DSL} provide high-level programming for microcontrollers, for example providing strong typing and memory safety. For example Copilot \citep{hess_arduino-copilot_2020} -and Ivory \citep{elliott_guilt_2015} are imperative \glspl{DSL} embedded in a functional language that compile to \gls{C}\slash\gls{CPP}. In contrast to \gls{CLEAN}/\gls{ITASK}/\gls{MTASK} such \glspl{DSL} are not tierless \gls{IOT} languages as they have no automatic integration with the server, i.e.\ with the application and presentation layers. +and Ivory \citep{elliott_guilt_2015} are imperative \glspl{DSL} embedded in a functional language that compile to \ccpp{}. In contrast to \cimtask{} such \glspl{DSL} are not tierless \gls{IOT} languages as they have no automatic integration with the server, i.e.\ with the application and presentation layers. \subsubsection{\texorpdfstring{\Glsxtrlong{FRP}}{Functional reactive programming}} @@ -190,16 +191,16 @@ Potato goes beyond other \gls{FRP} languages to provide a tierless \gls{FRP} \gl TOP allows for more complex collaboration patterns than \gls{FRP} \citep{wang_maintaining_2018}, and in consequence is unable to provide the strong guarantees on memory usage available in a restricted variant of \gls{FRP} such as arrowized \gls{FRP} \citep{nilsson_functional_2002}. -\subsubsection{Erlang/Elixir \texorpdfstring{\glsxtrshort{IOT}}{IoT} systems} +\subsubsection{Erlang\slash{}Elixir \texorpdfstring{\glsxtrshort{IOT}}{IoT} systems} A number of production \gls{IOT} systems are engineered in Erlang or Elixir, and many are mostly tierless. That is the perception, network and application layers are sets of distributed Erlang processes, although the presentation layer typically uses some conventional web technology. -A resource-rich sensor node may support many Erlang processes on an Erlang VM, or low level code (typically \gls{C}\slash\gls{CPP}) on a resource-constrained microcontroller can emulate an Erlang process. +A resource-rich sensor node may support many Erlang processes on an Erlang VM, or low level code (typically \ccpp{}) on a resource-constrained microcontroller can emulate an Erlang process. Only a small fraction of these systems are described in the academic literature, example exceptions are \citep{sivieri2012drop,shibanai_distributed_2018}, with many described only in grey literature or not at all. \subsection{Characteristics of tierless \texorpdfstring{\glsxtrshort{IOT}}{IoT} languages}% \label{sec_t4t:characteristics} -This study compares a pair of tierless \gls{IOT} languages with conventional tiered \gls{PYTHON} \gls{IOT} software. \Gls{CLEAN}\slash\gls{ITASK} and \gls{CLEAN}/\gls{ITASK}/\gls{MTASK} represent a specific set of tierless language design decisions, however many alternative designs are available. Crucially the limitations of the tierless \gls{CLEAN} languages, e.g.\ that they currently provide limited security, should not be seen as limitations of tierless technologies in general. This section briefly outlines key design decisions for tierless \gls{IOT} languages, discusses alternative designs, and describes the \gls{CLEAN} designs. The \gls{CLEAN} designs are illustrated in the examples in the following section. +This study compares a pair of tierless \gls{IOT} languages with conventional tiered \gls{PYTHON} \gls{IOT} software. \Citask{} and \cimtask{} represent a specific set of tierless language design decisions, however many alternative designs are available. Crucially the limitations of the tierless \gls{CLEAN} languages, e.g.\ that they currently provide limited security, should not be seen as limitations of tierless technologies in general. This section briefly outlines key design decisions for tierless \gls{IOT} languages, discusses alternative designs, and describes the \gls{CLEAN} designs. The \gls{CLEAN} designs are illustrated in the examples in the following section. \subsubsection{Tier splitting and placement} @@ -209,14 +210,13 @@ For example a tierless web language must identify client code to ship to browser Tierless web languages can make this determination statically, so-called \emph{tier splitting} using types or syntactic markers like \texttt{server} or \texttt{client} pragmas \citep{cooper2006links,10.1145/2775050.2633367}. It is even possible to infer the splitting, relieving the developers from the need to specify it, as illustrated for Javascript as a tierless web language \citep{10.1145/2661136.2661146}. -In \gls{CLEAN}\slash\gls{ITASK}\slash\gls{MTASK} and \gls{CLEAN}\slash\gls{ITASK} tier splitting is specified by functions, e.g.\ the \gls{CLEAN}\slash\gls{ITASK} \cleaninline{asyncTask} function identifies a task for execution on a remote device and \cleaninline{liftmTask} executes the given task on an \gls{IOT} device. +In \cimtask{} and \citask{} tier splitting is specified by functions, e.g.\ the \cimtask{} \cleaninline{asyncTask} function identifies a task for execution on a remote device and \cleaninline{liftmTask} executes the given task on an \gls{IOT} device. The tier splitting functions are illustrated in examples in the next section, e.g.\ on \cref{lst_t4t:itaskTempFull:startdevtask} in \cref{lst_t4t:itaskTempFull} and \cref{lst_t4t:mtasktemp:liftmtask} in \cref{lst_t4t:mtasktemp}. Specifying splitting as functions means that new splitting functions can be composed, and that splitting is under program control, e.g.\ during execution a program can decide to run a task locally or remotely. -As \gls{IOT} stacks are more complex than web stacks, the \emph{placement} of data and computations onto the devices/hosts in the system is more challenging. +As \gls{IOT} stacks are more complex than web stacks, the \emph{placement} of data and computations onto the devices\slash{}hosts in the system is more challenging. In many \gls{IOT} systems placement is manual: the sensor nodes are microcontrollers that are programmed by writing the program to flash memory. So physical access to the microcontroller is normally required to change the program, making updates challenging. -%Hence, most IoT systems compile sensor node code directly for the target architecture or via an existing language such as C/C++. Techniques like over-the-air programming and interpreters allow microcontrollers to be dynamically provisioned, increasing their maintainability and resilience. For example \citet{baccelli_reprogramming_2018} provide a single language \gls{IOT} system based on the RIOT \gls{OS} that allows runtime deployment of code snippets called containers. @@ -226,59 +226,30 @@ Mat\`e is an example of an early tierless sensor network framework where devices In general different tierless languages specify placement in different ways, e.g.\ code annotations or configuration files, and at different granularities, e.g.\ per function or per class \citep{weisenburger2020survey}. -\Gls{CLEAN}\slash\gls{ITASK}\slash\gls{MTASK} and \Gls{CLEAN}\slash\gls{ITASK} both use dynamic task placement. -In \gls{CLEAN}\slash\gls{ITASK}\slash\gls{MTASK} sensor nodes are programmed once with the \gls{MTASK} \gls{RTS}, and possibly some precompiled tasks. +\Cimtask{} and \citask{} both use dynamic task placement. +In \cimtask{} sensor nodes are programmed once with the \gls{MTASK} \gls{RTS}, and possibly some precompiled tasks. Thereafter a sensor node can dynamically receive \gls{MTASK} programs, compiled at runtime by the server. -In \gls{CLEAN}\slash\gls{ITASK} the sensor node runs an \gls{ITASK} server that recieves and executes code from the (\gls{IOT}) server \citep{oortgiese_distributed_2017}. +In \citask{} the sensor node runs an \gls{ITASK} server that recieves and executes code from the (\gls{IOT}) server \citep{oortgiese_distributed_2017}. Placement happens automatically as part of the first-class splitting constructs, so \cref{lst_t4t:mtasktemp:liftmtask} in \cref{lst_t4t:mtasktemp} places \cleaninline{devTask} onto the \cleaninline{dev} sensor node. -%\subsubsection{Program splitting} -% -%A key challenge for an automatically segmented tierless language is to determine which parts of the program correspond to a particular tier and hence should be executed by a specific component on a specific host, so-called tier splitting. -%For example a tierless web language must identify client code to ship to browsers, database code to execute in the DBMS, and application code to run on the server. Some tierless languages split programs using types, others use syntactic markers, e.g.\ pragmas like \cleaninline{server} or \cleaninline{client}, to split the program \citep{cooper2006links,10.1145/2775050.2633367}. It may be possible to infer the splitting between tiers, relieving the developers from the need specify it, as illustrated for Javascript as a tierless web language \citep{10.1145/2661136.2661146}. -% -%In \gls{CLEAN}\slash\gls{ITASK}/\gls{MTASK} and \gls{CLEAN}/\gls{ITASK} tier splitting is specified by functions, and hence is a first-class language construct. -%For example in \gls{CLEAN}\slash\gls{ITASK} the \cleaninline{asyncTask} function identifies a task for execution on a remote device and \cleaninline{liftmTask} executes the given task on an \gls{IOT} device. The tier splitting functions are illustrated in examples in the next section, e.g.\ on \cref{lst_t4t:itaskTempFull:startdevtask} in \cref{lst_t4t:itaskTempFull} and \cref{lst_t4t:mtasktemp:liftmtask} in \cref{lst_t4t:mtasktemp}. -%Specifying splitting as functions means that new splitting functions can be composed, and that splitting is under program control, e.g.\ during execution a program can decide to run a task locally or remotely. - \subsubsection{Communication}\label{ssec_t4t:communication} -Tierless languages may adopt a range of communication paradigms for communicating between components. Different tierless languages specify communication in different ways \citep{weisenburger2020survey}. Remote procedures are the most common communication mechanism: a procedure/function executing on a remote host/machine is called as if it was local. The communication of the arguments to, and the results from, the remote procedure is automatically provided by the language implementation. Other mechanisms include explicit message passing between components; publish/subscribe where components subscribe to topics of interest from other components; reactive programming defines event streams between remote components; finally shared state makes changes in a shared and potentially remote data structure visible to components. +Tierless languages may adopt a range of communication paradigms for communicating between components. Different tierless languages specify communication in different ways \citep{weisenburger2020survey}. Remote procedures are the most common communication mechanism: a procedure\slash{}function executing on a remote host\slash{}machine is called as if it was local. The communication of the arguments to, and the results from, the remote procedure is automatically provided by the language implementation. Other mechanisms include explicit message passing between components; publish\slash{}subscribe where components subscribe to topics of interest from other components; reactive programming defines event streams between remote components; finally shared state makes changes in a shared and potentially remote data structure visible to components. -\Gls{CLEAN}\slash\gls{ITASK}/\gls{MTASK} and \gls{CLEAN}/\gls{ITASK} communicate using a combination of remote task invocation, similar to remote procedures, and shared state through \glspl{SDS}. +\Cimtask{} and \citask{} communicate using a combination of remote task invocation, similar to remote procedures, and shared state through \glspl{SDS}. \Cref{lst_t4t:itaskTempFull} illustrates: \cref{lst_t4t:itaskTempFull:startdevtask} shows a server task launching a remote task, \cleaninline{devTask}, on to a sensor node; and \cref{lst_t4t:itaskTempFull:remoteShare} shows the sharing of the remote \cleaninline{latestTemp} \gls{SDS}. -%\subsubsection{Placement} -% -%In many \gls{IOT} systems the sensor nodes are microcontrollers that are programmed by writing the program to flash memory. This means that without extra effort, physical access to the microcontroller is needed to change the program making updates challenging. -%Hence, most \gls{IOT} systems compile sensor node code directly for the target architecture or via an existing language such as \gls{C}\slash\gls{CPP}. -% -%Techniques such as over-the-air programming and interpreters allow microcontrollers to be dynamically provisioned, increasing their maintainability and resilience. -%For example \citet{baccelli_reprogramming_2018} provide a single language \gls{IOT} system based on the RIOT \gls{OS} that allows runtime deployment of code snippets called containers. -%Both client and server are written in JavaScript. However, there is no integration between the client and the server other than that they are programmed from a single source. -%Mat\`e is an example of an early tierless sensor network framework where devices are provided with a virtual machine using TinyOS for dynamic provisioning \citep{levis_mate_2002}. -% -%Placement specifies how data and computations in a tierless program are assigned to the -%devices/hosts in the distributed system. Different tierless languages specify placement in different ways, e.g.\ code annotations or configuration files, and at different granularities, e.g.\ per function or per class \citep{weisenburger2020survey}. -% -%\Gls{CLEAN}\slash\gls{ITASK}/\gls{MTASK} and \gls{CLEAN}/\gls{ITASK} both use dynamic task placement, similar to dynamic function placement. -%In \gls{CLEAN}\slash\gls{ITASK}/\gls{MTASK} sensor nodes are programmed once with the \gls{MTASK} \gls{RTS}, and possibly some precompiled tasks. -%Thereafter a sensor node can dynamically receive \gls{MTASK} programs, compiled at runtime by the server. -%In \gls{CLEAN}\slash\gls{ITASK} the sensor node runs an \gls{ITASK} server that recieves and executes code from the (\gls{IOT}) server \citep{oortgiese_distributed_2017}. -%%The \gls{ITASK} server decides what code to execute depending on the serialised execution graph that the server sends \citep{oortgiese_distributed_2017}. -%Placement happens automatically as part of the first-class splitting constructs outlined in \cref{ssec_t4t:communication}, so \cref{lst_t4t:mtasktemp:liftmtask} in \cref{lst_t4t:mtasktemp} places \cleaninline{devTask} onto the \cleaninline{dev} sensor node. - \subsubsection{Security} Security is a major issue and a considerable challenge for many \gls{IOT} systems \citep{10.1145/3437537}. There are potentially security issues at each layer in an \gls{IOT} application (\cref{fig_t4t:iot_arch}). The security issues and defence mechanisms at the application and presentation layers are relatively standard, e.g.\ defending against SQL injection attacks. The security issues at the network and perception layers are more challenging. Resource-rich sensor nodes can adopt some standard security measures like encrypting messages, and regularly applying software patches to the operating system. However microcontrollers often lack the computational resources for encryption, and it is hard to patch their system software because the program is often stored in flash memory. In consequence there are infamous examples of \gls{IOT} systems being hijacked to create botnets \citep{203628,herwig_measurement_2019}. Securing the entire stack in a conventional tiered \gls{IOT} application is particularly challenging as the stack is implemented in a collection of programming languages with low level programming and communication abstractions. In such polyglot distributed systems it is hard to determine, and hence secure, the flow of data between components. In consequence a small mistake may have severe security implications. -A number of characteristics of tierless languages help to improve security. Communication and placement vulnerabilities are minimised as communication and placement are automatically generated and checked by the compiler. So injection attacks and the exploitation of communication/placement protocol bugs are less likely. Vulnerabilities introduced by mismatched types are avoided as the entire system is type checked. Moreover, tierless languages can exploit language level security techniques. For example languages like Jif/split \citep{zdancewic2002secure} and Swift \citep{chong2007secure} place components to protect the security of data. Another example are programming language technologies for controlling information flow, and these can be used to improve security. For example Haski uses them to improve the security of \gls{IOT} systems \citep{valliappan_towards_2020}. +A number of characteristics of tierless languages help to improve security. Communication and placement vulnerabilities are minimised as communication and placement are automatically generated and checked by the compiler. So injection attacks and the exploitation of communication\slash{}placement protocol bugs are less likely. Vulnerabilities introduced by mismatched types are avoided as the entire system is type checked. Moreover, tierless languages can exploit language level security techniques. For example languages like Jif\slash{}split \citep{zdancewic2002secure} and Swift \citep{chong2007secure} place components to protect the security of data. Another example are programming language technologies for controlling information flow, and these can be used to improve security. For example Haski uses them to improve the security of \gls{IOT} systems \citep{valliappan_towards_2020}. However many tierless languages have yet to provide a comprehensive set of security technologies, despite its importance in domains like web and \gls{IOT} applications. For example Erlang and many Erlang-based systems \citep{shibanai_distributed_2018,sivieri2012drop}, lack important security measures. Indeed security is not covered in a recent, otherwise comprehensive, survey of tierless technologies \citep{weisenburger2020survey}. -\Gls{CLEAN}\slash\gls{ITASK} and \gls{CLEAN}/\gls{ITASK}/\gls{MTASK} are typical in this respect: little effort has yet been expended on improving their security. Of course as tierless languages they benefit from static type safety and automatically generated communication and placement. Some preliminary work shows that, as the communication between layers is protocol agnostic, more secure alternatives can be used. One example is to run the \gls{ITASK} server behind a reverse proxy implementing TLS/SSL encryption \citep{wijkhuizen_security_2018}. A second is to add integrity checks or even encryption to the communication protocol for resource-rich sensor nodes \citep{de_boer_secure_2020}. +\Citask{} and \cimtask{} are typical in this respect: little effort has yet been expended on improving their security. Of course as tierless languages they benefit from static type safety and automatically generated communication and placement. Some preliminary work shows that, as the communication between layers is protocol agnostic, more secure alternatives can be used. One example is to run the \gls{ITASK} server behind a reverse proxy implementing TLS\slash{}SSL encryption \citep{wijkhuizen_security_2018}. A second is to add integrity checks or even encryption to the communication protocol for resource-rich sensor nodes \citep{de_boer_secure_2020}. \section{Task-oriented and \texorpdfstring{\glsxtrshort{IOT}}{IoT} programming in \texorpdfstring{\glsentrytext{CLEAN}}{Clean}} @@ -320,7 +291,7 @@ The tuning combinator \cleaninline{<<@} adds a label to the web editor displayin \begin{lstClean}[% numbers=left, - caption={SimpleTempSensor: a \gls{CLEAN}\slash\gls{ITASK} program to read a local room temperature sensor and display it on a web page}, + caption={SimpleTempSensor: a \citask{} program to read a local room temperature sensor and display it on a web page}, label={lst_t4t:itaskTemp}] module simpleTempSensor @@ -368,7 +339,7 @@ On \cref{lst_t4t:itaskTemp:launch} \cleaninline{task} is launched with an initia \begin{lstClean}[% numbers=left, - caption={TempHistory: a tierless \gls{CLEAN}\slash\gls{ITASK} webapplication that records and manipulates timed temperatures}, + caption={TempHistory: a tierless \citask{} webapplication that records and manipulates timed temperatures}, label={lst_t4t:TempHistory}] module TempHistory @@ -432,7 +403,7 @@ The \cleaninline{controlSDS} task illustrates communication from the web page us \fbox{\includegraphics[width=.95\textwidth]{TempHistory2}} \caption{Web page sorted by temperature.} \end{subfigure} - \caption{Web pages generated by the \cleaninline{TempHistory} \gls{CLEAN}\slash\gls{ITASK} tierless web application. + \caption{Web pages generated by the \cleaninline{TempHistory} \citask{} tierless web application. The \cleaninline{Take} button is only enabled when the topmost editor contains a positive number. }% \label{fig_t4t:TempHistory} @@ -452,7 +423,7 @@ The \cleaninline{controlSDS} task illustrates communication from the web page us A typical \gls{IOT} system goes beyond a web application by incorporating a distributed set of sensor nodes each with a collection of sensors or actuators. That is, they add the perception and network layers in \cref{fig_t4t:iot_arch}. If the sensor nodes have the computational resources to support an \gls{ITASK} server, as a Raspberry Pi does, then \gls{ITASK} can also be used to implement these layers, and integrate them with the application and presentation layers tierlessly. -As an example of tierless \gls{IOT} programming in \gls{CLEAN}\slash\gls{ITASK} \cref{lst_t4t:itaskTempFull} shows a complete temperature sensing system with a server and a single sensor node (\gls{CRTS}), omitting only the module name and imports. +As an example of tierless \gls{IOT} programming in \citask{} \cref{lst_t4t:itaskTempFull} shows a complete temperature sensing system with a server and a single sensor node (\gls{CRTS}), omitting only the module name and imports. It is similar to the SimpleTempSensor and TempHistory programs above, for example \cleaninline{devTask} repeatedly sleeps and records temperatures and times, and \cleaninline{mainTask} displays the temperatures on the web page in \cref{fig_t4t:cwtsweb}. There are some important differences, however. The \cleaninline{devTask} (\crefrange{lst_t4t:itaskTempFull:sensorfro}{lst_t4t:itaskTempFull:sensorto}) executes on the sensor node and records the temperatures in a standard timestamped (lens on) \pgls{SDS}: \cleaninline{dateTimeStampedShare} \cleaninline{latestTemp}. The \cleaninline{mainTask} (\cref{lst_t4t:itaskTempFull:main}) executes on the server: it starts \cleaninline{devTask} as an asynchronous task on the specified sensor node (\cref{lst_t4t:itaskTempFull:startdevtask}) and then generates a web page to display the latest temperature and time (\cref{lst_t4t:itaskTempFull:displaystart,lst_t4t:itaskTempFull:displayend}). @@ -467,7 +438,7 @@ The type for writing \cleaninline{latestTemp} is a tuple with a new \cleaninline \begin{lstClean}[% numbers=left, - caption={\gls{CRTS}: a tierless temperature sensing \gls{IOT} system. Written in \gls{CLEAN}\slash\gls{ITASK}, it targets a resource-rich sensor node.}, + caption={\gls{CRTS}: a tierless temperature sensing \gls{IOT} system. Written in \citask{}, it targets a resource-rich sensor node.}, label={lst_t4t:itaskTempFull}] tempSDS :: SimpleSDSLens [(DateTime, Real)] tempSDS = sharedStore "temperatures" [] @@ -523,7 +494,7 @@ Like \gls{ITASK}, \gls{MTASK} is task oriented, e.g.\ there are primitive tasks Apart from the \gls{EDSL}, the \gls{MTASK} system contains a featherlight domain-specific \emph{operating system} running on the microcontroller. This \gls{OS} task scheduler receives the byte code generated from one or more \gls{MTASK} programs and interleaves the execution of those tasks. The \gls{OS} also manages \gls{SDS} updates and the passing of task results. The \gls{MTASK} \gls{OS} is stored in flash memory while the tasks are stored in \gls{RAM} to minimise wear on the flash memory. While sending byte code to a sensor node at runtime greatly increases the amount of communication, this can be mitigated as any tasks known at compile time can be preloaded on the microcontroller. -In contrast, compiled programs, like \gls{C}\slash\gls{CPP}, are stored in flash memory and there can only ever be a few thousand programs uploaded during the lifetime of the microcontroller before exhausting the flash memory. +In contrast, compiled programs, like \ccpp{}, are stored in flash memory and there can only ever be a few thousand programs uploaded during the lifetime of the microcontroller before exhausting the flash memory. \subsection{Engineering tierless \texorpdfstring{\glsxtrshort{IOT}}{IoT} systems with \texorpdfstring{\glsentrytext{MTASK}}{mTask}}% \label{sec_t4t:mtaskIOT} @@ -553,7 +524,7 @@ For example, the interval used in the \cleaninline{rpeatevery} task (\cref{lst_t \begin{lstClean}[% numbers=left, caption={ - \Gls{CWTS}: a tierless temperature sensing \gls{IOT} system. Written in \gls{CLEAN}\slash\gls{ITASK}/\gls{MTASK}, it targets a resource-constrained sensor node. Each line is annotated with the functionality as analysed in \cref{sec_t4t:codesize}.}, + \Gls{CWTS}: a tierless temperature sensing \gls{IOT} system. Written in \cimtask{}, it targets a resource-constrained sensor node. Each line is annotated with the functionality as analysed in \cref{sec_t4t:codesize}.}, label={lst_t4t:mtasktemp}, ] module cwts @@ -602,7 +573,7 @@ mainTask \includegraphics[width=.95\textwidth]{cwtsDiagram2} \caption{Deployment diagram.} \end{subfigure} - \caption{Tierless \gls{ITASK}\slash\gls{MTASK} \gls{CWTS} temperature sensing \gls{IOT} system.}% + \caption{Tierless \cimtask{} \gls{CWTS} temperature sensing \gls{IOT} system.}% \label{fig_t4t:cwtsDiagram} \end{figure} @@ -646,10 +617,10 @@ The collector caches sensor data locally when the server is unreachable. In contrast to \gls{PRS}, \gls{PWS}'s sensor nodes are microcontrollers running \gls{MICROPYTHON}, a dialect of \gls{PYTHON} specifically designed to run on small, low powered embedded devices \citep{kodali2016low}. To enable a fair comparison between the software stacks we are careful to use the same object-oriented software architecture, e.g.\ using the same classes in \gls{PWS} and \gls{PRS}. -\Gls{PYTHON} and \gls{MICROPYTHON} are appropriate tiered comparison languages. Tiered \gls{IOT} systems are implemented in a whole range of programming languages, with \gls{PYTHON}, \gls{MICROPYTHON}, \gls{C} and \gls{CPP} being popular for some tiers in many implementations. \Gls{C}\slash\gls{CPP} implementations would probably result in more verbose programs and even less type safety. +\Gls{PYTHON} and \gls{MICROPYTHON} are appropriate tiered comparison languages. Tiered \gls{IOT} systems are implemented in a whole range of programming languages, with \gls{PYTHON}, \gls{MICROPYTHON}, \gls{C} and \gls{CPP} being popular for some tiers in many implementations. \Ccpp{} implementations would probably result in more verbose programs and even less type safety. The other reasons for selecting \gls{PYTHON} and \gls{MICROPYTHON} are pragmatic. \Gls{PRS} had been constructed in \gls{PYTHON}, deployed, and was being used as an \gls{IOT} experimental platform. Selecting \gls{MICROPYTHON} for the resource-constrained \gls{PWS} sensor nodes facilitates comparison by minimising changes to the resource-rich and resource-constrained codebases. -We anticipate that the codebase for a tiered smart campus implementation in another imperative/object-oriented language, like \gls{CPP}, would be broadly similar to the \gls{PRS} and \gls{PWS} codebases. +We anticipate that the codebase for a tiered smart campus implementation in another imperative\slash{}object-oriented language, like \gls{CPP}, would be broadly similar to the \gls{PRS} and \gls{PWS} codebases. \subsection{Tierless implementations} @@ -658,7 +629,7 @@ They use SQLite as a database backend. Communication between a sensor node and the server is initiated by the server. -\Gls{CRS}'s sensor nodes are Raspberry Pi 4s, and execute \gls{CLEAN}\slash\gls{ITASK} programs. +\Gls{CRS}'s sensor nodes are Raspberry Pi 4s, and execute \citask{} programs. Communication from the sensor node to the server is implicit and happens via \glspl{SDS} over \gls{TCP} using platform independent execution graph serialisation \citep{oortgiese_distributed_2017}. \Gls{CWS}'s sensor nodes are \gls{WEMOS} microcontrollers running \gls{MTASK} tasks. Communication and serialisation is, by design, very similar to \gls{ITASK}, i.e.\ via \glspl{SDS} over either a serial port connection, raw \gls{TCP}, or \gls{MQTT} over \gls{TCP}. @@ -746,7 +717,7 @@ The smart campus sensor node programs executing on the Raspberry Pis have far hi In \gls{PRS} the sensor node program is written in \gls{PYTHON}, a language far less focused on minimising memory usage than \gls{MICROPYTHON}. For example an object like a string is larger in \gls{PYTHON} than in \gls{MICROPYTHON} and consequently does not support all features such as \emph{f-strings}. Furthermore, not all advanced \gls{PYTHON} feature regarding classes are available in \gls{MICROPYTHON}, i.e.\ only a subset of the \gls{PYTHON} specification is supported \citep{diffmicro}. -In summary the sensor node code generated by both tierless languages, \gls{ITASK} and \gls{MTASK}, is sufficiently memory efficient for the target sensor node hardware. Indeed, the maximum residencies of the \gls{CLEAN} sensor node code is less than the corresponding hand-written (Micro)\gls{PYTHON} code. Of course in a tiered stack the hand-written code can be more easily optimised to minimise residency, and this could even entail using a memory efficient language like \gls{C}\slash\gls{CPP}. However, such optimisation requires additional developer effort, and a new language would introduce additional semantic friction. +In summary the sensor node code generated by both tierless languages, \gls{ITASK} and \gls{MTASK}, is sufficiently memory efficient for the target sensor node hardware. Indeed, the maximum residencies of the \gls{CLEAN} sensor node code is less than the corresponding hand-written (Micro)\gls{PYTHON} code. Of course in a tiered stack the hand-written code can be more easily optimised to minimise residency, and this could even entail using a memory efficient language like \ccpp{}. However, such optimisation requires additional developer effort, and a new language would introduce additional semantic friction. \paragraph{Power} Sensor nodes and sensors are designed to have low power demands, and this is particularly important if they are operating on batteries. The grey literature consensus is that with all sensors enabled a sensor node should typically have sub-\qty{1}{\watt} peak power draw. The \gls{WEMOS} sensor nodes used in \gls{CWS} and \gls{PWS} have the low power consumption of a typical embedded device: with all sensors enabled, they consume around \qty{0.2}{\watt}. @@ -781,7 +752,7 @@ Web Interface (WI) code provides the web interface from the server, i.e.\ the pr Database Interface (DI) code communicates between the server and the database\strut(s). Communication (CO) code provides communication between the server and the sensor nodes, and executes on both sensor node and server, i.e.\ the network layer. -The most striking information in \cref{table_t4t:multi} is that \emph{the tierless implementations require far less code than the tiered implementations}. For example 166/562 \gls{SLOC} for \gls{CWS}\slash\gls{PWS}, or 70\% fewer \gls{SLOC}. We attribute the code reduction to three factors: reduced interoperation, automatic communication, and high level programming abstractions. We analyse each of these aspects in the following subsections. +The most striking information in \cref{table_t4t:multi} is that \emph{the tierless implementations require far less code than the tiered implementations}. For example 166\slash{}562 \gls{SLOC} for \gls{CWS}\slash\gls{PWS}, or 70\% fewer \gls{SLOC}. We attribute the code reduction to three factors: reduced interoperation, automatic communication, and high level programming abstractions. We analyse each of these aspects in the following subsections. \begin{table} \centering % used for centering table @@ -808,27 +779,40 @@ The most striking information in \cref{table_t4t:multi} is that \emph{the tierle \end{table} \paragraph{Code proportions.} -Comparing the percentages of code required to implement the smart campus functionalities normalises the data and avoids some issues when comparing \gls{SLOC} for different programming languages, and especially for languages with different paradigms like object-oriented \gls{PYTHON} and functional \gls{CLEAN}. \Cref{fig_t4t:multipercentage} shows the percentage of the total \gls{SLOC} required to implement the smart campus functionalities in each of the four implementations, and is computed from the data in \cref{table_t4t:multi}. It shows that there are significant differences between the percentage of code for each functionality between the tiered and tierless implementations. For example 17\% of the tiered implementations specifies communication, whereas this requires only 3\% of the tierless implementations, i.e.\ 6$\times$ less. We explore the reasons for this in \cref{sec_t4t:Communication}. The other major difference is the massive percentage of Database Interface code in the tierless implementations: at least 47\%. The smart campus specification required a standard DBMS, and the \gls{CLEAN}\slash\gls{ITASK} SQL interface occupies some 78 \gls{SLOC}. While this is a little less than the 106 \gls{SLOC} used in \gls{PYTHON} (\cref{table_t4t:multi}), it is a far higher percentage of systems with total codebases of only around 160 \gls{SLOC}. Idiomatic \gls{CLEAN}/\gls{ITASK} would use high level abstractions to store persistent data in \pgls{SDS}, requiring just a few \gls{SLOC}. +Comparing the percentages of code required to implement the smart campus functionalities normalises the data and avoids some issues when comparing \gls{SLOC} for different programming languages, and especially for languages with different paradigms like object-oriented \gls{PYTHON} and functional \gls{CLEAN}. +\Cref{fig_t4t:multipercentage} shows the percentage of the total \gls{SLOC} required to implement the smart campus functionalities in each of the four implementations, and is computed from the data in \cref{table_t4t:multi}. +It shows that there are significant differences between the percentage of code for each functionality between the tiered and tierless implementations. +For example 17\% of the tiered implementations specifies communication, whereas this requires only 3\% of the tierless implementations, i.e.\ 6$\times$ less. +We explore the reasons for this in \cref{sec_t4t:Communication}. +The other major difference is the massive percentage of Database Interface code in the tierless implementations: at least 47\%. +The smart campus specification required a standard DBMS, and the \citask{} SQL interface occupies some 78 \gls{SLOC}. +While this is a little less than the 106 \gls{SLOC} used in \gls{PYTHON} (\cref{table_t4t:multi}), it is a far higher percentage of systems with total codebases of only around 160 \gls{SLOC}. +Idiomatic \citask{} would use high level abstractions to store persistent data in \pgls{SDS}, requiring just a few \gls{SLOC}. The total size of \gls{CWS} and \gls{CRS} would be reduced by a factor of two and the percentage of Database Interface code would be even less than in the tiered \gls{PYTHON} implementations. \begin{figure} \centering \includegraphics[width=.7\linewidth]{bar_chart.pdf} - \caption{Comparing the percentage of code required to implement each functionality in tiered/tierless and resource-rich/constrained smart campus implementations.}% + \caption{Comparing the percentage of code required to implement each functionality in tiered\slash{}tierless and resource-rich\slash{}constrained smart campus implementations.}% \label{fig_t4t:multipercentage} \end{figure} \subsection{Comparing codebases for resource-rich\slash{}constrained sensor nodes}% \label{sec_t4t:resourcerich} -Before exploring the reasons for the smaller tierless codebase we compare the implementations for resource-rich and resource-constrained sensor nodes, again using \gls{SLOC} and code proportions. \Cref{table_t4t:multi} shows that the two tiered implementations are very similar in size: with \gls{PWS} for microcontrollers requiring 562 \gls{SLOC} and \gls{PRS} for supersensors requiring 576 \gls{SLOC}. +Before exploring the reasons for the smaller tierless codebase we compare the implementations for resource-rich and resource-constrained sensor nodes, again using \gls{SLOC} and code proportions. +\Cref{table_t4t:multi} shows that the two tiered implementations are very similar in size: with \gls{PWS} for microcontrollers requiring 562 \gls{SLOC} and \gls{PRS} for supersensors requiring 576 \gls{SLOC}. The two tierless implementations are also similar in size: \gls{CWS} requiring 166 and \gls{CRS} 155 \gls{SLOC}. -There are several main reasons for the similarity. One is that the server-side code, i.e.\ for the presentation and application layers, is identical for both resource rich/constrained implementations. The identical server code accounts for approximately 40\% of the \gls{PWS} and \gls{PRS} codebases, and approximately 85\% of the \gls{CWS} and \gls{CRS} codebases (\cref{fig_t4t:multipercentage}). For the perception and network layers on the sensor nodes, the \gls{PYTHON} and \gls{MICROPYTHON} implementations have the same structure, e.g.\ a class for each type of sensor, and use analogous libraries. Indeed, approaches like CircuitPython \citep{CircuitPython} allow the same code to execute on both resource-rich and resource-constrained sensor nodes. +There are several main reasons for the similarity. +One is that the server-side code, i.e.\ for the presentation and application layers, is identical for both resource rich\slash{}constrained implementations. +The identical server code accounts for approximately 40\% of the \gls{PWS} and \gls{PRS} codebases, and approximately 85\% of the \gls{CWS} and \gls{CRS} codebases (\cref{fig_t4t:multipercentage}). +For the perception and network layers on the sensor nodes, the \gls{PYTHON} and \gls{MICROPYTHON} implementations have the same structure, e.g.\ a class for each type of sensor, and use analogous libraries. +Indeed, approaches like CircuitPython \citep{CircuitPython} allow the same code to execute on both resource-rich and resource-constrained sensor nodes. -Like \gls{PYTHON} and \gls{MICROPYTHON}, \gls{ITASK} and \gls{MTASK} are designed to be similar, as elaborated in \cref{sec_t4t:ComparingTierless}. The similarity is apparent when comparing the \gls{ITASK} \gls{CRTS} and \gls{ITASK}\slash\gls{MTASK} \gls{CWTS} room temperature systems in \cref{lst_t4t:itaskTempFull,lst_t4t:mtasktemp}. That is, both implementations use similar \glspl{SDS} and lenses; they have similar \cleaninline{devTask}s that execute on the sensor node, and the server-side \cleaninline{mainTask}s are almost identical: they deploy the remote \cleaninline{devTask} before generating the web page to report the readings. +Like \gls{PYTHON} and \gls{MICROPYTHON}, \gls{ITASK} and \gls{MTASK} are designed to be similar, as elaborated in \cref{sec_t4t:ComparingTierless}. The similarity is apparent when comparing the \gls{ITASK} \gls{CRTS} and \cimtask{} \gls{CWTS} room temperature systems in \cref{lst_t4t:itaskTempFull,lst_t4t:mtasktemp}. That is, both implementations use similar \glspl{SDS} and lenses; they have similar \cleaninline{devTask}s that execute on the sensor node, and the server-side \cleaninline{mainTask}s are almost identical: they deploy the remote \cleaninline{devTask} before generating the web page to report the readings. In both \gls{PYTHON} and \gls{CLEAN} the resource-constrained implementations are less than 7\% larger than the resource-rich implementations. This suggests that \emph{the development and maintenance effort of simple \gls{IOT} systems for resource-constrained and for resource-rich sensor nodes is similar in tierless technologies,} just as it is in tiered technologies. A caveat is that the smart campus system is relatively simple, and developing more complex perception and network code on bare metal may prove more challenging. That is, the lack of \gls{OS} support, and the restricted languages and libraries, may have greater impact. We return to this issue in \cref{sec_t4t:ComparingTierless}. @@ -958,7 +942,7 @@ Although the temperature sensor applications are small compared to the smart cam The architecture consists of a server and a single sensor node (\cref{fig_t4t:cwtsDiagram}). The sensor node measures and reports the temperature every ten seconds to the server while the server displays the latest temperature via a web interface to the user. -\Cref{table_t4t:temp} compares the \gls{SLOC} required for the \gls{MICROPYTHON} and \gls{CLEAN}\slash\gls{ITASK}/\gls{MTASK} \gls{WEMOS} temperature sensors: \gls{PWTS} and \gls{CWTS} respectively. The code sizes here should not be used to compare the programming models as implementing such a small application as a conventional \gls{IOT} stack requires a significant amount of configuration and other machinery that would be reused in a larger application. Hence, the ratio between total \gls{PWTS} and \gls{CWTS} code sizes (298:15) is far greater than for realistic applications like \gls{PWS} and \gls{CWS} (471:166). +\Cref{table_t4t:temp} compares the \gls{SLOC} required for the \gls{MICROPYTHON} and \cimtask{} \gls{WEMOS} temperature sensors: \gls{PWTS} and \gls{CWTS} respectively. The code sizes here should not be used to compare the programming models as implementing such a small application as a conventional \gls{IOT} stack requires a significant amount of configuration and other machinery that would be reused in a larger application. Hence, the ratio between total \gls{PWTS} and \gls{CWTS} code sizes (298:15) is far greater than for realistic applications like \gls{PWS} and \gls{CWS} (471:166). \begin{table} \centering @@ -985,7 +969,7 @@ The multiple tiers in \gls{PRS} and \gls{PWS} provide different levels of abstra %in order to facilitate interoperation of the components. However, there are various ways that high-level abstractions make the \gls{CWS} much shorter than \gls{PRS} and \gls{PWS} implementations. -Firstly, \gls{FP} languages are generally more concise than most other programming languages because their powerful abstractions like higher-order and/or polymorphic functions require less code to describe a computation. +Firstly, \gls{FP} languages are generally more concise than most other programming languages because their powerful abstractions like higher-order and\slash{}or polymorphic functions require less code to describe a computation. Secondly, the \gls{TOP} paradigm used in \gls{ITASK} and \gls{MTASK} reduces the code size further by making it easy to specify \gls{IOT} functionality concisely. As examples, the step combinator \cleaninline{>>*.} allows the task value on the left-hand side to be observed until one of the steps is enabled; and the \cleaninline{viewSharedInformation} (line 31 of \cref{lst_t4t:mtasktemp}) part of the UI will be automatically updated when the value of the \gls{SDS} changes. Moreover, each \gls{SDS} provides automatic updates to all coupled \glspl{SDS} and associated tasks. Thirdly, the amount of explicit type information is minimised in comparison to other languages, as much is automatically inferred \citep{hughes1989functional}. @@ -1034,7 +1018,7 @@ Some \gls{IOT} applications, including smart campus and other building monitorin In many \gls{IOT} architectures, including \gls{PRS} and \gls{PWS}, detecting failure is challenging because the application layer listens to the devices. When a device comes online, it registered with the application and starts sending data. When a device goes offline again, it could be because the power was out, the device was broken or the device just paused the connection. -If a sensor node fails in \gls{CWS}, the \gls{ITASK}\slash\gls{MTASK} combinator interacting with a sensor node will throw an \gls{ITASK} exception. +If a sensor node fails in \gls{CWS}, the \imtask{} combinator interacting with a sensor node will throw an \gls{ITASK} exception. The exception is propagated and a handler can respond, e.g.\ rescheduling the task on a different device in the room, or requesting that a manager replaces the device. That is, \gls{ITASK}, uses standard succinct declarative exception handling. \begin{lstClean}[caption={An \gls{MTASK} failover combinator.},label={lst_t4t:failover}] @@ -1058,7 +1042,7 @@ Currently, \gls{PRS} and \gls{PWS} both use heartbeats to confirm that the senso Far more engineering effort is expended on maintaining a system, than on the initial development. Tiered and tierless \gls{IOT} systems have very different maintainability properties. -The modularity of the tiered stack makes replacing tiers/components easy. For example in \gls{PWS} or \gls{PRS} the MongoDB NoSQL DBMS could be readily be replaced by an alternative like {CouchDB}. Because a tierless compiler must generate code for components, replacing them may not be so easy. If there are \gls{ITASK} abstractions for the component then replacement is straightforward. For example replacing SQLite with some other SQL DBMS simply entails recompilation of the application. +The modularity of the tiered stack makes replacing tiers\slash{}components easy. For example in \gls{PWS} or \gls{PRS} the MongoDB NoSQL DBMS could be readily be replaced by an alternative like {CouchDB}. Because a tierless compiler must generate code for components, replacing them may not be so easy. If there are \gls{ITASK} abstractions for the component then replacement is straightforward. For example replacing SQLite with some other SQL DBMS simply entails recompilation of the application. However incorporating a component that does not yet have a task abstraction, like a NoSQL DBMS, is more involved. That is, a foreign function interface to the new component must be implemented, along with a suitable \gls{ITASK} abstraction for operations on the component. Many maintenance tasks are smaller in scale and occur within the components or tiers. Consider a simple change, for example if the temperature value recorded by a sensor changes from integer to real. @@ -1145,7 +1129,7 @@ On the other hand, it is possible to outsource this logic to the \gls{ITASK} pro \subsection{The benefits of a bare metal execution environment} -Despite the language restrictions, components of a tierless language executing on a microcontroller can exploit the bare metal environment. Many of these benefits are shared by other bare metal languages like \gls{MICROPYTHON} or \gls{C}\slash\gls{CPP}. So as \gls{MTASK} executes on bare metal it has some advantages over \gls{ITASK}. Most notably \gls{MTASK} has better control of timing as on bare metal there are no other processes or threads that compete for CPU cycles. +Despite the language restrictions, components of a tierless language executing on a microcontroller can exploit the bare metal environment. Many of these benefits are shared by other bare metal languages like \gls{MICROPYTHON} or \ccpp{}. So as \gls{MTASK} executes on bare metal it has some advantages over \gls{ITASK}. Most notably \gls{MTASK} has better control of timing as on bare metal there are no other processes or threads that compete for CPU cycles. This makes the \gls{MTASK} \cleaninline{repeatEvery} (\cref{lst_t4t:mtasktemp}, \cref{lst_t4t:mtasktemp:sn2}) much more accurate than the \gls{ITASK} \cleaninline{waitForTimer} (\cref{lst_t4t:itaskTempFull}, \cref{lst_t4t:itaskTempFull:waitForTimer}). While exact timing is not important in this example, it is significant for many other \gls{IOT} applications. In contrast \gls{ITASK} cannot give real time guarantees. One reason is that an \gls{ITASK} server can ship an arbitrary number of \gls{ITASK} or \gls{MTASK} tasks to a device. @@ -1185,7 +1169,7 @@ Our empirical results for \gls{IOT} systems are consistent with the benefits cla \end{enumerate*} We show that \emph{tierless languages have the potential to significantly improve the reliability of \gls{IOT} systems}. We illustrate how \gls{CLEAN} maintains type safety, contrasting this with a loss of type safety in \gls{PRS}. -We illustrate higher order failure management in \gls{CLEAN}\slash\gls{ITASK}/\gls{MTASK} in contrast to the \gls{PYTHON}-based failure management in \gls{PRS}. For maintainability a tiered approach makes replacing components easy, but refactoring within the components is far harder than in a tierless \gls{IOT} language. Again our findings are consistent with the simplied \textit{Code Maintenance} benefits claimed for tierless languages \citep{weisenburger2020survey}. +We illustrate higher order failure management in \cimtask{} in contrast to the \gls{PYTHON}-based failure management in \gls{PRS}. For maintainability a tiered approach makes replacing components easy, but refactoring within the components is far harder than in a tierless \gls{IOT} language. Again our findings are consistent with the simplied \textit{Code Maintenance} benefits claimed for tierless languages \citep{weisenburger2020survey}. Finally, we contrast community support for the technologies (\cref{sec_t4t:Discussion}). We report \emph{the first comparison of a tierless \gls{IOT} codebase for resource-rich sensor nodes with one for resource-constrained sensor nodes}. @@ -1195,7 +1179,11 @@ We report \emph{the first comparison of a tierless \gls{IOT} codebase for resour as it is in the tiered \gls{PYTHON} implementations (\cref{fig_t4t:multipercentage}). This suggests that the code for resource-constrained and resource-rich sensor nodes can be broadly similar in tierless technologies, as it is in many tiered technologies (\cref{sec_t4t:resourcerich}). \end{enumerate*} -We present \emph{the first comparison of two tierless \gls{IOT} languages: one designed for resource-constrained sensor nodes (\gls{CLEAN} with \gls{ITASK} and \gls{MTASK}), and the other for resource-rich sensor nodes (\gls{CLEAN} with \gls{ITASK}).} \gls{CLEAN}\slash\gls{ITASK} can implement all layers of the \gls{IOT} stack if the sensor nodes have the computational resources, as the Raspberry Pis do in \gls{CRS}. On resource constrained sensor nodes \gls{MTASK} are required to implement the perception and network layers, as on the \gls{WEMOS} minis in \gls{CWS}. We show that a bare metal execution environment allows \gls{MTASK} to have better control of peripherals, timing and energy consumption. The memory available on a microcontroller restricts the programming abstractions available in \gls{MTASK} to a fixed set of combinators, no user defined or recursive data types, strict evaluation, and makes it harder to add new abstractions. Even with these restrictions \gls{MTASK} provide a higher level of abstraction than most bare metal languages, and can readily express many \gls{IOT} applications including the \gls{CWS} \gls{UOG} smart campus application (\cref{sec_t4t:ComparingTierless}). +We present \emph{the first comparison of two tierless \gls{IOT} languages: one designed for resource-constrained sensor nodes (\cimtask{}), and the other for resource-rich sensor nodes (\citask{}).} \Citask{} can implement all layers of the \gls{IOT} stack if the sensor nodes have the computational resources, as the Raspberry Pis do in \gls{CRS}. +On resource constrained sensor nodes \gls{MTASK} are required to implement the perception and network layers, as on the \gls{WEMOS} minis in \gls{CWS}. +We show that a bare metal execution environment allows \gls{MTASK} to have better control of peripherals, timing and energy consumption. +The memory available on a microcontroller restricts the programming abstractions available in \gls{MTASK} to a fixed set of combinators, no user defined or recursive data types, strict evaluation, and makes it harder to add new abstractions. +Even with these restrictions \gls{MTASK} provide a higher level of abstraction than most bare metal languages, and can readily express many \gls{IOT} applications including the \gls{CWS} \gls{UOG} smart campus application (\cref{sec_t4t:ComparingTierless}). Our empirical results are consistent with the benefits of tierless languages listed in Section 2.1 of \citep{weisenburger2020survey}. \subsection{Reflections} -- 2.20.1