From 3fe94feeee0efd1922263aca9d578031e2283f61 Mon Sep 17 00:00:00 2001 From: Mart Lubbers Date: Fri, 7 Oct 2022 15:12:13 +0200 Subject: [PATCH] directory structure --- appendix/bytecode.tex | 106 ----- {appendix => appx}/.chktexrc | 0 appx/bytecode.tex | 107 ++++++ .../clean_for_haskell_programmers.tex | 19 +- {appendix => appx}/latexmkrc | 0 {appendix => appx}/lst/.gitignore | 0 {appendix => appx}/lst/Makefile | 0 {appendix => appx}/lst/example_deep.hs | 0 {appendix => appx}/lst/expr_gadt.hs | 0 {appendix => appx}/lst/expr_gadt.icl | 0 {appendix => appx}/lst/generic_eq.icl | 0 {appendix => appx}/lst/generic_print.icl | 0 {appendix => appx}/mtask_aux.tex | 3 +- {backmatter => back}/acknowledgements.tex | 5 +- {backmatter => back}/curriculum_vitae.tex | 3 +- .../research_data_management.tex | 3 +- {backmatter => back}/samenvatting.tex | 3 +- {backmatter => back}/summary.tex | 3 +- {conclusion => concl}/conclusion.tex | 3 +- ...ed_multi-view_stack-based_computations.tex | 12 - {domain-specific_languages => dsl}/.chktexrc | 0 .../class_deep_embedding.tex | 34 +- .../dsl_techniques.tex | 10 +- .../first-class_datatypes.tex | 10 +- {frontmatter => front}/by.eps | 0 {frontmatter => front}/cc.eps | 0 {frontmatter => front}/dedication.tex | 0 {frontmatter => front}/motto.tex | 0 {frontmatter => front}/nd.eps | 0 {frontmatter => front}/titlepage.tex | 18 +- glossaries.tex | 6 + .../.chktexrc | 0 intro/hyponymy_of_dsls.tex | 15 + {introduction => intro}/img/orcid.png | Bin {introduction => intro}/introduction.tex | 102 +++-- {introduction => intro}/iot-layers.tex | 0 {introduction => intro}/tosd.tex | 0 {introduction => intro}/traditional.tex | 0 lstlangarduino.sty | 2 +- lstlanghaskell.sty | 185 ++++++--- lstlanghaskelllhstex.sty | 146 ------- other.bib | 149 +++++++- preamble.tex | 71 ++-- process_bib.sh | 7 + self.bib | 2 - thesis.tex | 65 ++-- tiered_vs._tierless_programming/img/arch.pdf | Bin 112741 -> 0 bytes .../img/bar_chart.pdf | Bin 20180 -> 0 bytes tiot.bib | 42 -- {mtask => top}/.chktexrc | 0 {mtask => top}/beyond_microprocessors.tex | 3 +- {mtask => top}/integration.tex | 3 +- {mtask => top}/interpreting.tex | 3 +- {mtask => top}/mtask.tex | 286 ++++++++++---- {mtask => top}/mtask_by_example.tex | 3 +- .../mtask_integration.tex | 0 tvt/.chktexrc | 24 ++ .../img/TempHistory.png | Bin .../img/TempHistory1.png | Bin .../img/TempHistory2.png | Bin .../img/arch.drawio | 0 .../img/cwss_web.png | Bin .../img/cwts.png | Bin .../img/cwtsDiagram.png | Bin .../img/cwtsDiagram2.png | Bin .../img/devTaskDiagram.png | Bin .../img/devTaskDiagram2.png | Bin .../img/gen_stacked_barchart.py | 0 .../img/iTaskTemp.png | Bin .../img/iTaskTemp1.png | Bin .../img/iTaskTemp2.png | Bin .../img/iTaskTempRemote.png | Bin .../img/mTaskTemp.png | Bin .../img/prss.jpg | Bin .../img/readTempTask.png | Bin .../img/sensordata.png | Bin .../img/simpleTempSensor.png | Bin .../img/todolist1.png | Bin .../img/todolist3.png | Bin .../img/todolist4.png | Bin .../img/todolist5.png | Bin .../img/todolist6.png | Bin .../img/todolist7.png | Bin .../img/web_iot.png | Bin .../img/wemos.jpg | Bin .../smart_campus.tex => tvt/tvt.tex | 361 +++++++++--------- 86 files changed, 998 insertions(+), 816 deletions(-) delete mode 100644 appendix/bytecode.tex rename {appendix => appx}/.chktexrc (100%) create mode 100644 appx/bytecode.tex rename {appendix => appx}/clean_for_haskell_programmers.tex (93%) rename {appendix => appx}/latexmkrc (100%) rename {appendix => appx}/lst/.gitignore (100%) rename {appendix => appx}/lst/Makefile (100%) rename {appendix => appx}/lst/example_deep.hs (100%) rename {appendix => appx}/lst/expr_gadt.hs (100%) rename {appendix => appx}/lst/expr_gadt.icl (100%) rename {appendix => appx}/lst/generic_eq.icl (100%) rename {appendix => appx}/lst/generic_print.icl (100%) rename {appendix => appx}/mtask_aux.tex (98%) rename {backmatter => back}/acknowledgements.tex (95%) rename {backmatter => back}/curriculum_vitae.tex (96%) rename {backmatter => back}/research_data_management.tex (96%) rename {backmatter => back}/samenvatting.tex (85%) rename {backmatter => back}/summary.tex (85%) rename {conclusion => concl}/conclusion.tex (79%) delete mode 100644 domain-specific_languages/strongly-typed_multi-view_stack-based_computations.tex rename {domain-specific_languages => dsl}/.chktexrc (100%) rename {domain-specific_languages => dsl}/class_deep_embedding.tex (98%) rename {domain-specific_languages => dsl}/dsl_techniques.tex (97%) rename {domain-specific_languages => dsl}/first-class_datatypes.tex (99%) rename {frontmatter => front}/by.eps (100%) rename {frontmatter => front}/cc.eps (100%) rename {frontmatter => front}/dedication.tex (100%) rename {frontmatter => front}/motto.tex (100%) rename {frontmatter => front}/nd.eps (100%) rename {frontmatter => front}/titlepage.tex (87%) rename {tiered_vs._tierless_programming => intro}/.chktexrc (100%) create mode 100644 intro/hyponymy_of_dsls.tex rename {introduction => intro}/img/orcid.png (100%) rename {introduction => intro}/introduction.tex (80%) rename {introduction => intro}/iot-layers.tex (100%) rename {introduction => intro}/tosd.tex (100%) rename {introduction => intro}/traditional.tex (100%) delete mode 100644 lstlanghaskelllhstex.sty create mode 100755 process_bib.sh delete mode 100644 tiered_vs._tierless_programming/img/arch.pdf delete mode 100644 tiered_vs._tierless_programming/img/bar_chart.pdf rename {mtask => top}/.chktexrc (100%) rename {mtask => top}/beyond_microprocessors.tex (57%) rename {mtask => top}/integration.tex (68%) rename {mtask => top}/interpreting.tex (71%) rename {mtask => top}/mtask.tex (71%) rename {mtask => top}/mtask_by_example.tex (86%) rename introduction/hyponymy_of_dsls.tex => top/mtask_integration.tex (100%) create mode 100644 tvt/.chktexrc rename {tiered_vs._tierless_programming => tvt}/img/TempHistory.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/TempHistory1.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/TempHistory2.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/arch.drawio (100%) rename {tiered_vs._tierless_programming => tvt}/img/cwss_web.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/cwts.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/cwtsDiagram.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/cwtsDiagram2.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/devTaskDiagram.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/devTaskDiagram2.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/gen_stacked_barchart.py (100%) rename {tiered_vs._tierless_programming => tvt}/img/iTaskTemp.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/iTaskTemp1.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/iTaskTemp2.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/iTaskTempRemote.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/mTaskTemp.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/prss.jpg (100%) rename {tiered_vs._tierless_programming => tvt}/img/readTempTask.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/sensordata.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/simpleTempSensor.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/todolist1.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/todolist3.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/todolist4.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/todolist5.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/todolist6.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/todolist7.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/web_iot.png (100%) rename {tiered_vs._tierless_programming => tvt}/img/wemos.jpg (100%) rename tiered_vs._tierless_programming/smart_campus.tex => tvt/tvt.tex (77%) diff --git a/appendix/bytecode.tex b/appendix/bytecode.tex deleted file mode 100644 index b46e53e..0000000 --- a/appendix/bytecode.tex +++ /dev/null @@ -1,106 +0,0 @@ -\documentclass[../thesis.tex]{subfiles} - -\begin{document} -\ifSubfilesClassLoaded{ - \pagenumbering{arabic} -}{} - -\myappendix{chp:bytecode_instruction_set}{Bytecode instruction set}% -\todo[inline]{formatting} - -\begin{tabular}{ll} - $l$ & label\\ - $w_r$ & return width\\ - $w_a$ & argument width\\ - $fp$ & frame pointer\\ - $sp$ & stack pointer\\ - $pc$ & program counter\\ -\end{tabular} - -Bytecode is byte encoded, stack has 16-bit cells. -Longs and reals are stored with the MSB first. - -\footnotesize -\begin{longtable}{lllll} - \caption{\normalsize Semantics for the bytecode instructions\label{tbl:instr_task}} - \endfirsthead% - \caption{\normalsize Semantics for the bytecode instructions (cont.)} - \endhead% - \endfoot% - \endlastfoot% - \toprule - Instr. & Args & Semantics & sp & pc\\ - - \midrule - \texttt{return} & $w_r~w_a$ & $st[fp\shortminus{}w_a\shortminus{}3+i] = st[fp+1]$ & $st[fp\shortminus{}w_a\shortminus{}3+w_r]$ & $st[fp\shortminus{}w_a\shortminus{}1]$\\ - & & {\bf for all} $i\in\{0..w_r\}$\\ - & & $fp = st[fp\shortminus{}w_a\shortminus{}2]$\\ - \texttt{jumpF} & $l$ & & $sp\shortminus{}1$ & $\left\{\begin{array}{ll} pc+1 & \textrm{\bf if } st[sp\shortminus{}1]\\l & \textrm{\bf otherwise}\end{array}\right.$\\ - \texttt{jump} & $l$ & & $sp\shortminus{}1$ & $l$\\ - \texttt{jumpSR} & $w_a~l$ & $st[sp\shortminus{}w_a\shortminus{}1]=pc+2$ & & $l$\\ - \texttt{tailCall} & $w_{a_1}~w_{a_2}~l$ & $rotate\:(w_{a_1}+3+w_{a_2},w_{a_2})$ & $fp$ & $jl$\\ - & & $fp = fp-w_{a_1}+w_{a_2}$\\ - & & \multicolumn{3}{l}{{\bf where} $w_{a_1}$ is the width of the current function and $w_{a_2}$ the width of the called function}\\ - \texttt{arg} & $i$ & $st[sp] = st[fp-1-i]$ & $sp+1$\\ - \texttt{push} & $n~b_0\ldots b_n$ & $st[sp+i] = s[i]$ & $sp+n$ & $pc+2+n$\\ - & & {\bf for all} $i\in\{0..n\}$\\ -% \midrule - \texttt{pop} & $n$ & & $sp-n$ & $pc+2$\\ -% \midrule - \texttt{rot} & $d~n$ & $rotate\:(d, n)$ & $sp$ & $pc+3$\\ -% \midrule - \texttt{dup} & & $st[sp] = st[sp-1]$ & $sp+1$ & $pc+1$\\ -% \midrule - \texttt{pushPtrs} & & $st[sp] = sp$ & $sp+3$ & $pc+1$\\ - & & $st[sp+1] = fp$\\ - & & $st[sp+2] = 0$\\ -% \midrule - \texttt{unOp} & & $st[sp-1] = \diamond{}st[sp-1]$ & $sp$ & $pc+1$\\ - & & \multicolumn{3}{l}{{\bf for all} $\diamond\in\{\neg\}$}\\ -% \midrule - \texttt{binOp} & & $st[sp-2] = st[sp-2] \mathbin{\oplus} st[sp-1]$ & $sp-1$ & $pc+1$\\ - & & \multicolumn{3}{l}{{\bf for all} $\oplus\in\{+, -, *, /, \wedge, \vee, \equiv, \not\equiv, \leq, \geq, <, >\}$}\\ -% \midrule - \texttt{mkTask} & \texttt{Stable\textsubscript{n}} & $st[sp-n-1] = node (stable,$ & $sp-n+1$ & $pc+2$\\ - & & $\qquad\qquad st[sp-1], \ldots, st[sp-n-1])$\\ - \texttt{mkTask} & \texttt{Unstable\textsubscript{n}} & $st[sp-n-1] = node (unstable,$ & $sp-n+1$ & $pc+2$\\ - & & $\qquad\qquad st[sp-1], \ldots, st[sp-n-1])$\\ -% \midrule -% & \multicolumn{3}{l}{Unstable\textsubscript{n}} & st[sp-n-1] = node (unstable, & sp-n+1 & pc+2\\ -% & \multicolumn{3}{l}{} & \qquad\qquad st[sp-1], \textrm{\ldots}, st[sp-n-1])\\ -% \midrule -% -% & \multicolumn{3}{l}{ReadD } & st[sp-1] \ \ = node (readd, st[sp-1]) & & pc+2\\ -% \midrule -% & \multicolumn{3}{l}{ReadA } & st[sp-1] \ \ = node (reada, st[sp-1]) & & pc+2\\ -% \midrule -% & \multicolumn{3}{l}{Repeat } & st[sp-1] \ \ = node (repeat, st[sp-1]) & & pc+2\\ -% \midrule -% & \multicolumn{3}{l}{Delay } & st[sp-1] \ \ = node (delay, st[sp-1]) & & pc+2\\ -% \midrule -% -% & \multicolumn{3}{l}{WriteD } & st[sp-2] \ \ = node (writed, st[sp-1], st[sp-2]) & sp-1 & pc+2\\ -% \midrule -% & \multicolumn{3}{l}{WriteA } & st[sp-2] \ \ = node (writea, st[sp-1], st[sp-2]) & sp-1 & pc+2\\ -% \midrule -% & \multicolumn{3}{l}{And } & st[sp-2] \ \ = node (and, st[sp-1], st[sp-2]) & sp-1 & pc+2\\ -% \midrule -% & \multicolumn{3}{l}{Or } & st[sp-2] \ \ = node (or, st[sp-1], st[sp-2]) & sp-1 & pc+2\\ -% \midrule -% -% & \multicolumn{3}{l}{SdsSet i } & st[sp-1] \ \ = node (sdsset,i, st[sp-1]) & & pc+3\\ -% \midrule -% -% & \multicolumn{3}{l}{SdsGet i } & st[sp]\ \ \ \ \ = node (sdsget, i) & sp+1 & pc+3\\ -% \midrule -% & \multicolumn{3}{l}{DHTTemp i } & st[sp-1] \ \ = node (dhttemp, i) & sp+1 & pc+3\\ -% \midrule -% & \multicolumn{3}{l}{DHTHumid i } & st[sp-1] \ \ = node (dhthumid, i) & sp+1 & pc+3\\ -% \midrule -% -% & \multicolumn{3}{l}{Step aw jl } & st[sp-1] \ \ = node (step, aw, jl, st[sp-1]) & sp-1 & pc+5\\ - \bottomrule -\end{longtable} - -\input{subfilepostamble} -\end{document} diff --git a/appendix/.chktexrc b/appx/.chktexrc similarity index 100% rename from appendix/.chktexrc rename to appx/.chktexrc diff --git a/appx/bytecode.tex b/appx/bytecode.tex new file mode 100644 index 0000000..b6e13f8 --- /dev/null +++ b/appx/bytecode.tex @@ -0,0 +1,107 @@ +\documentclass[../thesis.tex]{subfiles} + +\begin{document} +\ifSubfilesClassLoaded{ + \pagenumbering{arabic} +}{} + +\chapter{Bytecode instruction set}% +\label{chp:bytecode_instruction_set}% +\todo[inline]{formatting} + +\begin{tabular}{ll} + $l$ & label\\ + $w_r$ & return width\\ + $w_a$ & argument width\\ + $fp$ & frame pointer\\ + $sp$ & stack pointer\\ + $pc$ & program counter\\ +\end{tabular} + +Bytecode is byte encoded, stack has 16-bit cells. +Longs and reals are stored with the MSB first. + +\footnotesize +%\begin{longtable}{lllll} +% \caption{\normalsize Semantics for the bytecode instructions\label{tbl:instr_task}} +% \endfirsthead% +% \caption{\normalsize Semantics for the bytecode instructions (cont.)} +% \endhead% +% \endfoot% +% \endlastfoot% +% \toprule +% Instr. & Args & Semantics & sp & pc\\ +% +% \midrule +% \texttt{return} & $w_r~w_a$ & $st[fp\shortminus{}w_a\shortminus{}3+i] = st[fp+1]$ & $st[fp\shortminus{}w_a\shortminus{}3+w_r]$ & $st[fp\shortminus{}w_a\shortminus{}1]$\\ +% & & {\bf for all} $i\in\{0..w_r\}$\\ +% & & $fp = st[fp\shortminus{}w_a\shortminus{}2]$\\ +% \texttt{jumpF} & $l$ & & $sp\shortminus{}1$ & $\left\{\begin{array}{ll} pc+1 & \textrm{\bf if } st[sp\shortminus{}1]\\l & \textrm{\bf otherwise}\end{array}\right.$\\ +% \texttt{jump} & $l$ & & $sp\shortminus{}1$ & $l$\\ +% \texttt{jumpSR} & $w_a~l$ & $st[sp\shortminus{}w_a\shortminus{}1]=pc+2$ & & $l$\\ +% \texttt{tailCall} & $w_{a_1}~w_{a_2}~l$ & $rotate\:(w_{a_1}+3+w_{a_2},w_{a_2})$ & $fp$ & $jl$\\ +% & & $fp = fp-w_{a_1}+w_{a_2}$\\ +% & & \multicolumn{3}{p{.75\textwidth}}{{\bf where} $w_{a_1}$ is the width of the current function and $w_{a_2}$ the width of the called function}\\ +% \texttt{arg} & $i$ & $st[sp] = st[fp-1-i]$ & $sp+1$\\ +% \texttt{push} & $n~b_0\ldots b_n$ & $st[sp+i] = s[i]$ & $sp+n$ & $pc+2+n$\\ +% & & {\bf for all} $i\in\{0..n\}$\\ +%% \midrule +% \texttt{pop} & $n$ & & $sp-n$ & $pc+2$\\ +%% \midrule +% \texttt{rot} & $d~n$ & $rotate\:(d, n)$ & $sp$ & $pc+3$\\ +%% \midrule +% \texttt{dup} & & $st[sp] = st[sp-1]$ & $sp+1$ & $pc+1$\\ +%% \midrule +% \texttt{pushPtrs} & & $st[sp] = sp$ & $sp+3$ & $pc+1$\\ +% & & $st[sp+1] = fp$\\ +% & & $st[sp+2] = 0$\\ +%% \midrule +% \texttt{unOp} & & $st[sp-1] = \diamond{}st[sp-1]$ & $sp$ & $pc+1$\\ +% & & \multicolumn{3}{l}{{\bf for all} $\diamond\in\{\neg\}$}\\ +%% \midrule +% \texttt{binOp} & & $st[sp-2] = st[sp-2] \mathbin{\oplus} st[sp-1]$ & $sp-1$ & $pc+1$\\ +% & & \multicolumn{3}{l}{{\bf for all} $\oplus\in\{+, -, *, /, \wedge, \vee, \equiv, \not\equiv, \leq, \geq, <, >\}$}\\ +%% \midrule +% \texttt{mkTask} & \texttt{Stable\textsubscript{n}} & $st[sp-n-1] = node (stable,$ & $sp-n+1$ & $pc+2$\\ +% & & $\qquad\qquad st[sp-1], \ldots, st[sp-n-1])$\\ +% \texttt{mkTask} & \texttt{Unstable\textsubscript{n}} & $st[sp-n-1] = node (unstable,$ & $sp-n+1$ & $pc+2$\\ +% & & $\qquad\qquad st[sp-1], \ldots, st[sp-n-1])$\\ +%% \midrule +%% & \multicolumn{3}{l}{Unstable\textsubscript{n}} & st[sp-n-1] = node (unstable, & sp-n+1 & pc+2\\ +%% & \multicolumn{3}{l}{} & \qquad\qquad st[sp-1], \textrm{\ldots}, st[sp-n-1])\\ +%% \midrule +%% +%% & \multicolumn{3}{l}{ReadD } & st[sp-1] \ \ = node (readd, st[sp-1]) & & pc+2\\ +%% \midrule +%% & \multicolumn{3}{l}{ReadA } & st[sp-1] \ \ = node (reada, st[sp-1]) & & pc+2\\ +%% \midrule +%% & \multicolumn{3}{l}{Repeat } & st[sp-1] \ \ = node (repeat, st[sp-1]) & & pc+2\\ +%% \midrule +%% & \multicolumn{3}{l}{Delay } & st[sp-1] \ \ = node (delay, st[sp-1]) & & pc+2\\ +%% \midrule +%% +%% & \multicolumn{3}{l}{WriteD } & st[sp-2] \ \ = node (writed, st[sp-1], st[sp-2]) & sp-1 & pc+2\\ +%% \midrule +%% & \multicolumn{3}{l}{WriteA } & st[sp-2] \ \ = node (writea, st[sp-1], st[sp-2]) & sp-1 & pc+2\\ +%% \midrule +%% & \multicolumn{3}{l}{And } & st[sp-2] \ \ = node (and, st[sp-1], st[sp-2]) & sp-1 & pc+2\\ +%% \midrule +%% & \multicolumn{3}{l}{Or } & st[sp-2] \ \ = node (or, st[sp-1], st[sp-2]) & sp-1 & pc+2\\ +%% \midrule +%% +%% & \multicolumn{3}{l}{SdsSet i } & st[sp-1] \ \ = node (sdsset,i, st[sp-1]) & & pc+3\\ +%% \midrule +%% +%% & \multicolumn{3}{l}{SdsGet i } & st[sp]\ \ \ \ \ = node (sdsget, i) & sp+1 & pc+3\\ +%% \midrule +%% & \multicolumn{3}{l}{DHTTemp i } & st[sp-1] \ \ = node (dhttemp, i) & sp+1 & pc+3\\ +%% \midrule +%% & \multicolumn{3}{l}{DHTHumid i } & st[sp-1] \ \ = node (dhthumid, i) & sp+1 & pc+3\\ +%% \midrule +%% +%% & \multicolumn{3}{l}{Step aw jl } & st[sp-1] \ \ = node (step, aw, jl, st[sp-1]) & sp-1 & pc+5\\ +% \bottomrule +%\end{longtable} + +\input{subfilepostamble} +\end{document} diff --git a/appendix/clean_for_haskell_programmers.tex b/appx/clean_for_haskell_programmers.tex similarity index 93% rename from appendix/clean_for_haskell_programmers.tex rename to appx/clean_for_haskell_programmers.tex index 4d78011..cc7873d 100644 --- a/appendix/clean_for_haskell_programmers.tex +++ b/appx/clean_for_haskell_programmers.tex @@ -7,7 +7,8 @@ }{ } -\myappendix{chp:clean_for_haskell_programmers}{\texorpdfstring{\glsentrytext{CLEAN}}{Clean} for \texorpdfstring{\glsentrytext{HASKELL}}{Haskell} Programmers}% +\chapter{\texorpdfstring{\glsentrytext{CLEAN}}{Clean} for \texorpdfstring{\glsentrytext{HASKELL}}{Haskell} Programmers}% +\label{chp:clean_for_haskell_programmers} This note is meant to give people who are familiar with the functional programming language \gls{HASKELL} a consise overview of \gls{CLEAN} language elements and how they differ from \gls{HASKELL}. The goal is to support the reader when reading \gls{CLEAN} code. @@ -70,7 +71,7 @@ f :: v:a u:b -> u:b, [v<=u] // f works when a is less unique than b %:: T = T (Int -> *(*World -> *World)) // Writing :: T = T (Int *World -> *World) won't work \subsection{Expressions} -Patterns in \gls{CLEAN} can be used as predicates as well~\cite[Chp.~3.4.3]{plasmeijer_clean_2021}. +Patterns in \gls{CLEAN} can be used as predicates as well~\citep[Chp.~3.4.3]{plasmeijer_clean_2021}. Using the \cleaninline{=:} operator, a value can be tested against a pattern. Variable names are not allowed but wildcard patterns \cleaninline{\_} are. @@ -86,7 +87,7 @@ ifAB x ifa ifb = if (x =: (A _)) ifa ifb Due to the nature of uniqueness typing, many functions in \gls{CLEAN} are state transition functions with possibly unique states. The \emph{let before} construct allows the programmer to specify sequential actions without having to invent unique names for the different versions of the state. -\Cref{lst:let_before} shows an example of the usage of the \emph{let before} construct (adapted from~\cite[Chp.~3.5.4]{plasmeijer_clean_2021}). +\Cref{lst:let_before} shows an example of the usage of the \emph{let before} construct (adapted from~\citep[Chp.~3.5.4]{plasmeijer_clean_2021}). \begin{lstClean}[label={lst:let_before},caption={Let before expression example.}] readChars :: *File -> ([Char], *File) @@ -98,7 +99,7 @@ readChars file \end{lstClean} \subsection{Generics} -Polytypic functions~\citep{jeuring_polytypic_1996}---also known as generic or kind-indexed fuctions---are built into \gls{CLEAN}~\cite[Chp.~7.1]{plasmeijer_clean_2021}\citep{alimarine_generic_2005} whereas in \gls{HASKELL} they are implemented as a library~\cite[Chp.~6.19.1]{ghc_team_ghc_2021}. +Polytypic functions~\citep{jeuring_polytypic_1996}---also known as generic or kind-indexed fuctions---are built into \gls{CLEAN}~\citep[Chp.~7.1]{plasmeijer_clean_2021}\citep{alimarine_generic_2005} whereas in \gls{HASKELL} they are implemented as a library~\citep[Chp.~6.19.1]{ghc_team_ghc_2021}. The implementation of generics in \gls{CLEAN} is very similar to that of Generic H$\forall$skell~\citep{hinze_generic_2003}. %When calling a generic function, the kind must always be specified and depending on the kind, the function may require more arguments. @@ -110,12 +111,13 @@ Metadata about the types is available using the \cleaninline{of} syntax that giv \subsection{\texorpdfstring{\glsentrytext{GADT}}{GADT}s} \Glspl{GADT} are enriched data types that allow the type instantiation of the constructor to be explicitly defined~\citep{cheney_first-class_2003,hinze_fun_2003}. -While \glspl{GADT} are not natively supported in \gls{CLEAN}, they can be simulated using embedding-projection pairs or equivalence types~\cite[Sec.~2.2]{cheney_lightweight_2002}. +While \glspl{GADT} are not natively supported in \gls{CLEAN}, they can be simulated using embedding-projection pairs or equivalence types~\citep[Sec.~2.2]{cheney_lightweight_2002}. To illustrate this, \cref{lst:gadt_clean} shows an example \gls{GADT} that would be implemented in \gls{HASKELL} as done in \cref{lst:gadt_haskell}\requiresGHCmod{GADTs}. -\lstinputlisting[language=Clean,firstline=4,label={lst:gadt_clean},caption={Expression \gls{GADT} using equivalence types in \gls{CLEAN}.}]{lst/expr_gadt.icl} -\lstinputlisting[language=Haskell,style=haskell,firstline=4,label={lst:gadt_haskell},caption={Expression \gls{GADT} in \gls{HASKELL}.}]{lst/expr_gadt.hs} +\lstinputlisting[language=Clean,firstline=4,lastline=24,label={lst:gadt_clean},caption={Expression \gls{GADT} using equivalence types in \gls{CLEAN}.}]{lst/expr_gadt.icl} +\lstinputlisting[language={[Regular]Haskell},firstline=4,label={lst:gadt_haskell},caption={Expression \gls{GADT} in \gls{HASKELL}.}]{lst/expr_gadt.hs} +\clearpage \section{Syntax} \begin{longtable}{p{.45\linewidth}p{.5\linewidth}} \caption[]{Syntactical differences between \gls{CLEAN} and \gls{HASKELL}.}% @@ -226,6 +228,7 @@ To illustrate this, \cref{lst:gadt_clean} shows an example \gls{GADT} that would \cleaninline{:: R = \{ f :: t \}} & \haskellinline{data R = R \{ f :: t \}}\\ \cleaninline{r = \{ f = e \}} & \haskellinline{r = R \{e\}}\\ \cleaninline{r.f} & \haskellinline{f r}\\ + & \haskellinline{r.f}\requiresGHCmod{Requires \gls{GHC} version 9.2.0 or higher}{OverloadedRecordDot}\\ \cleaninline{r!f}\footnote{This operator allows for field selection from unique records.} & \haskellinline{(\\v->(f v, v)) r}\\ \cleaninline{\{r \& f = e \}} & \haskellinline{r \{ f = e \}}\\ @@ -247,7 +250,7 @@ To illustrate this, \cref{lst:gadt_clean} shows an example \gls{GADT} that would \cleaninline{a = \{e \\\\ p <-: a\}} & \haskellinline{a = array (0, length a-1)}\\ & \quad\haskellinline{[e \| (i, a) <- [0..] `zip` a]}\\ \cleaninline{a.[i]} & \haskellinline{a!i}\\ - \cleaninline{a![i]}\footnote{This operator allows for field selection from unique arrays.} & \haskellinline{(\v->(v!i, v)) a}\\ + \cleaninline{a![i]}\footnote{This operator allows for field selection from unique arrays.} & \haskellinline{(\\v->(v!i, v)) a}\\ \cleaninline{\{ a \& [i] = e\}} & \haskellinline{a//[(i, e)]}\\ \midrule diff --git a/appendix/latexmkrc b/appx/latexmkrc similarity index 100% rename from appendix/latexmkrc rename to appx/latexmkrc diff --git a/appendix/lst/.gitignore b/appx/lst/.gitignore similarity index 100% rename from appendix/lst/.gitignore rename to appx/lst/.gitignore diff --git a/appendix/lst/Makefile b/appx/lst/Makefile similarity index 100% rename from appendix/lst/Makefile rename to appx/lst/Makefile diff --git a/appendix/lst/example_deep.hs b/appx/lst/example_deep.hs similarity index 100% rename from appendix/lst/example_deep.hs rename to appx/lst/example_deep.hs diff --git a/appendix/lst/expr_gadt.hs b/appx/lst/expr_gadt.hs similarity index 100% rename from appendix/lst/expr_gadt.hs rename to appx/lst/expr_gadt.hs diff --git a/appendix/lst/expr_gadt.icl b/appx/lst/expr_gadt.icl similarity index 100% rename from appendix/lst/expr_gadt.icl rename to appx/lst/expr_gadt.icl diff --git a/appendix/lst/generic_eq.icl b/appx/lst/generic_eq.icl similarity index 100% rename from appendix/lst/generic_eq.icl rename to appx/lst/generic_eq.icl diff --git a/appendix/lst/generic_print.icl b/appx/lst/generic_print.icl similarity index 100% rename from appendix/lst/generic_print.icl rename to appx/lst/generic_print.icl diff --git a/appendix/mtask_aux.tex b/appx/mtask_aux.tex similarity index 98% rename from appendix/mtask_aux.tex rename to appx/mtask_aux.tex index cd67dce..4a87974 100644 --- a/appendix/mtask_aux.tex +++ b/appx/mtask_aux.tex @@ -5,7 +5,8 @@ \pagenumbering{arabic} }{} -\myappendix{chp:mtask_aux}{Auxiliary \texorpdfstring{\glsentrytext{MTASK}}{mTask} type classes} +\chapter{Auxiliary \texorpdfstring{\glsentrytext{MTASK}}{mTask} type classes}% +\label{chp:mtask_aux} \lstset{basicstyle=\tt\footnotesize} \section{Peripherals}\label{sec:aux_peripherals} This section shows the peripherals not mentioned in \cref{chp:top4iot}. diff --git a/backmatter/acknowledgements.tex b/back/acknowledgements.tex similarity index 95% rename from backmatter/acknowledgements.tex rename to back/acknowledgements.tex index 9c8c30c..71761d0 100644 --- a/backmatter/acknowledgements.tex +++ b/back/acknowledgements.tex @@ -4,7 +4,8 @@ \ifSubfilesClassLoaded{ \pagenumbering{arabic} }{} -\mybackmatter{chp:acknowledgements}{Acknowledgements}% +\chapter{Acknowledgements}% +\label{chp:acknowledgements} %\begin{center} \noindent Funding: Teun de Groot, Ton van Heusden @@ -36,7 +37,7 @@ Additionally, this thesis acknowledges the support of the Erasmus+ Key Action 2 The information and views set out in this thesis are those of the author and do not necessarily reflect the official opinion of the European Union. Neither the European Union institutions and bodies nor any person acting on their behalf may be held responsible for the use which may be made of the information contained therein. -Finally I want to thank all anonymous reviewers for the indispensable suggestions and remarks on all papers. +Finally I want to thank all anonymous reviewers for the indispensable comments, suggestions and remarks on all papers. %\end{center} \input{subfilepostamble} \end{document} diff --git a/backmatter/curriculum_vitae.tex b/back/curriculum_vitae.tex similarity index 96% rename from backmatter/curriculum_vitae.tex rename to back/curriculum_vitae.tex index 9deaada..30524cd 100644 --- a/backmatter/curriculum_vitae.tex +++ b/back/curriculum_vitae.tex @@ -4,7 +4,8 @@ \ifSubfilesClassLoaded{ \pagenumbering{arabic} }{} -\mybackmatter{chp:curriculum_vitae}{Curriculum Vit\ae}% +\chapter{Curriculum Vit\ae}% +\label{chp:curriculum_vitae} Mart Lubbers \vspace{\baselineskip} diff --git a/backmatter/research_data_management.tex b/back/research_data_management.tex similarity index 96% rename from backmatter/research_data_management.tex rename to back/research_data_management.tex index bd48373..0c826b0 100644 --- a/backmatter/research_data_management.tex +++ b/back/research_data_management.tex @@ -5,7 +5,8 @@ \pagenumbering{arabic} }{} -\mybackmatter{chp:research_data_management}{Research Data Management}% +\chapter{Research Data Management}% +\label{chp:research_data_management} This thesis research has been carried out under the research data management policy of the Institute for Computing and Information Science of Radboud University, the Netherlands\footnote{\url{https://www.ru.nl/icis/research-data-management/}, last accessed \formatdate{20}{1}{2020}.}. diff --git a/backmatter/samenvatting.tex b/back/samenvatting.tex similarity index 85% rename from backmatter/samenvatting.tex rename to back/samenvatting.tex index d980132..0c1ee31 100644 --- a/backmatter/samenvatting.tex +++ b/back/samenvatting.tex @@ -5,7 +5,8 @@ \pagenumbering{arabic} }{} -\mybackmatter{chp:samenvatting}{Samenvatting}% +\chapter{Samenvatting}% +\label{chp:samenvatting} \selectlanguage{dutch} \begin{center} diff --git a/backmatter/summary.tex b/back/summary.tex similarity index 85% rename from backmatter/summary.tex rename to back/summary.tex index f96ac22..7e4f260 100644 --- a/backmatter/summary.tex +++ b/back/summary.tex @@ -5,7 +5,8 @@ \pagenumbering{arabic} }{} -\mybackmatter{chp:summary}{Summary}% +\chapter{Summary}% +\label{chp:summary} \begin{center} \noindent% diff --git a/conclusion/conclusion.tex b/concl/conclusion.tex similarity index 79% rename from conclusion/conclusion.tex rename to concl/conclusion.tex index 0dd6005..ea20f10 100644 --- a/conclusion/conclusion.tex +++ b/concl/conclusion.tex @@ -5,7 +5,8 @@ \pagenumbering{arabic} }{} -\mychapter{chp:conclusion}{Coda}% +\chapter{Coda}% +\label{chp:conclusion} \input{subfilepostamble} \end{document} diff --git a/domain-specific_languages/strongly-typed_multi-view_stack-based_computations.tex b/domain-specific_languages/strongly-typed_multi-view_stack-based_computations.tex deleted file mode 100644 index 8ece8f8..0000000 --- a/domain-specific_languages/strongly-typed_multi-view_stack-based_computations.tex +++ /dev/null @@ -1,12 +0,0 @@ -\documentclass[../thesis.tex]{subfiles} - -\begin{document} -\ifSubfilesClassLoaded{ - \pagenumbering{arabic} -}{} - -\mychapter{chp:strongly-typed_multi-view_stack-based_computations}{Strongly-Typed Multi-View Stack-Based Computations}% -TFP22 - -\input{subfilepostamble} -\end{document} diff --git a/domain-specific_languages/.chktexrc b/dsl/.chktexrc similarity index 100% rename from domain-specific_languages/.chktexrc rename to dsl/.chktexrc diff --git a/domain-specific_languages/class_deep_embedding.tex b/dsl/class_deep_embedding.tex similarity index 98% rename from domain-specific_languages/class_deep_embedding.tex rename to dsl/class_deep_embedding.tex index 171d981..bd3b59a 100644 --- a/domain-specific_languages/class_deep_embedding.tex +++ b/dsl/class_deep_embedding.tex @@ -5,7 +5,8 @@ \pagenumbering{arabic} }{} -\mychapter{chp:classy_deep_embedding}{Deep embedding with class}% +\chapter{Deep embedding with class}% +\label{chp:classy_deep_embedding} \begin{chapterabstract} The two flavours of DSL embedding are shallow and deep embedding. @@ -21,7 +22,7 @@ Additionally, little type-level trickery or complicated boilerplate code is required to achieve this. \end{chapterabstract} -\section{Introduction}% +\section{Introduction} The two flavours of DSL embedding are deep and shallow embedding~\citep{boulton_experience_1992}. In functional programming languages, shallow embedding models language constructs as functions in the host language. As a result, adding new language constructs---extra functions---is easy. @@ -54,8 +55,7 @@ Haskell~\citep{peyton_jones_haskell_2003} program using some minor extensions pr \footnotetext{Lubbers, M. (2021): Literate Haskell/lhs2\TeX{} source code of the paper ``Deep Embedding with Class'': TFP 2022.\ DANS.\ \url{https://doi.org/10.5281/zenodo.5081386}.} -\section{Deep embedding}% - +\section{Deep embedding} Pick a DSL, any DSL, pick the language of literal integers and addition. In deep embedding, terms in the language are represented by data in the host language. Hence, defining the constructs is as simple as creating the following algebraic data type\footnote{All data types and functions are subscripted to indicate the evolution.}. @@ -98,8 +98,7 @@ Extending the DSL with language constructs exposes the Achilles' heel of deep em Adding a case to the data type means that all semantics functions have become partial and need to be updated to be able to handle this new case. This does not seem like an insurmountable problem, but it does pose a problem if either the functions or the data type itself are written by others or are contained in a closed library. -\section{Shallow embedding}% - +\section{Shallow embedding} Conversely, let us see how this would be done in shallow embedding. First, the data type is represented by functions in the host language with embedded semantics. Therefore, the evaluators for literals and addition both become a function in the host language as follows. @@ -143,7 +142,7 @@ Semantics become data types\footnotemark{} implementing these type classes, resu In this case \haskelllhstexinline{newtype}s are used instead of regular \haskelllhstexinline{data} declarations. A \haskelllhstexinline{newtype} is a special data type with a single constructor containing a single value only to which it is isomorphic. It allows the programmer to define separate class instances that the instances of the isomorphic type without any overhead. - During compilation the constructor is completely removed~\cite[Sec.~4.2.3]{peyton_jones_haskell_2003}. + During compilation the constructor is completely removed~\citep[Sec.~4.2.3]{peyton_jones_haskell_2003}. } \begin{lstHaskellLhstex} @@ -220,7 +219,7 @@ It is only possible to create an expression with a subtraction on the top level. The recursive knot is left untied and as a result, \haskelllhstexinline{Sub_1} can never be reached from an \haskelllhstexinline{Expr_1}. Luckily, we can reconnect them by adding a special constructor to the \haskelllhstexinline{Expr_1} data type for housing extensions. -It contains an existentially quantified~\citep{mitchell_abstract_1988} type with type class constraints~\citep{laufer_combining_1994,laufer_type_1996} for all semantics type classes~\cite[Chp.~6.4.6]{ghc_team_ghc_2021} to allow it to house not just subtraction but any future extension. +It contains an existentially quantified~\citep{mitchell_abstract_1988} type with type class constraints~\citep{laufer_combining_1994,laufer_type_1996} for all semantics type classes~\citep[Chp.~6.4.6]{ghc_team_ghc_2021} to allow it to house not just subtraction but any future extension. \begin{lstHaskellLhstex} data Expr_2 = Lit_2 Int @@ -247,7 +246,7 @@ sub_2 e1 e2 = Ext_2 (Sub_2 e1 e2) In our example this means that the programmer can write\footnotemark{}: \footnotetext{% - Backticks are used to use functions or constructors in an infix fashion~\cite[Sec.~4.3.3]{peyton_jones_haskell_2003}. + Backticks are used to use functions or constructors in an infix fashion~\citep[Sec.~4.3.3]{peyton_jones_haskell_2003}. } \begin{lstHaskellLhstex} e2 :: Expr_2 @@ -370,8 +369,7 @@ instance HasPrint_3 d => Print_3 (Sub_3 d) where print_3 (Sub_3 e1 e2) = "(" ++ print_3 e1 ++ "-" ++ print_3 e2 ++ ")" \end{lstHaskellLhstex} -\section{Transformation semantics}% - +\section{Transformation semantics} Most semantics convert a term to some final representation and can be expressed just by functions on the cases. However, the implementation of semantics such as transformation or optimisation may benefit from a so-called intentional analysis of the abstract syntax tree. In shallow embedding, the implementation for these types of semantics is difficult because there is no tangible abstract syntax tree. @@ -424,8 +422,7 @@ When a literal zero is matched as the right-hand side of a subtraction, the left However, the type signature of the function dictates that it should be of type \haskelllhstexinline{Sub_3}. To overcome this problem we add a convolution constructor. -\subsection{Convolution}% - +\subsection{Convolution} Adding a loopback case or convolution constructor to \haskelllhstexinline{Sub_3} allows the removal of the \haskelllhstexinline{Sub_3} constructor while remaining the \haskelllhstexinline{Sub_3} type. It should be noted that a loopback case is \emph{only} required if the transformation actually removes tags. This changes the \haskelllhstexinline{Sub_3} data type as follows. @@ -449,8 +446,7 @@ instance HasOpt_4 d => Opt_4 (Sub_4 d) where opt_4 (SubLoop_4 e) = SubLoop_4 (opt_4 e) \end{lstHaskellLhstex} -\subsection{Pattern matching}% - +\subsection{Pattern matching} Pattern matching within datatypes and from an extension to the main data type works out of the box. Cross-extensional pattern matching on the other hand---matching on a particular extension---is something that requires a bit of extra care. Take for example negation propagation and double negation elimination. @@ -543,10 +539,13 @@ e3 :: (Typeable d, GDict (d (Neg_4 d)), GDict (d (Sub_4 d))) => Expr_4 d e3 = neg_4 (Lit_4 42 `sub_4` Lit_4 38) `Add_4` Lit_4 1 \end{lstHaskellLhstex} +\section{Chaining (reprise)}\label{sec:classy_reprise} +\todo{\ldots} + \section{Generalised algebraic data types}% Generalised algebraic data types (GADTs) are enriched data types that allow the type instantiation of the constructor to be explicitly defined~\citep{cheney_first-class_2003,hinze_fun_2003}. Leveraging GADTs, deeply embedded DSLs can be made statically type safe even when different value types are supported. -Even when GADTs are not supported natively in the language, they can be simulated using embedding-projection pairs or equivalence types~\cite[Sec.~2.2]{cheney_lightweight_2002}. +Even when GADTs are not supported natively in the language, they can be simulated using embedding-projection pairs or equivalence types~\citep[Sec.~2.2]{cheney_lightweight_2002}. Where some solutions to the expression problem do not easily generalise to GADTs (see \cref{sec:cde:related}), classy deep embedding does. Generalising the data structure of our DSL is fairly straightforward and to spice things up a bit, we add an equality and boolean not language construct. To make the existing DSL constructs more general, we relax the types of those constructors. @@ -596,7 +595,7 @@ class Opt_g v where Now that the shape of the type classes has changed, the dictionary data types and the type classes need to be adapted as well. The introduced type variable \haskelllhstexinline{a} is not an argument to the type class, so it should not be an argument to the dictionary data type. -To represent this type class function, a rank-2 polymorphic function is needed~\cite[Chp.~6.4.15]{ghc_team_ghc_2021}\citep{odersky_putting_1996}. +To represent this type class function, a rank-2 polymorphic function is needed~\citep[Chp.~6.4.15]{ghc_team_ghc_2021}\citep{odersky_putting_1996}. Concretely, for the evaluatior this results in the following definitions: \begin{lstHaskellLhstex} @@ -681,7 +680,6 @@ This paper differs from those approaches in the sense that it does not require a This research is partly funded by the Royal Netherlands Navy. Furthermore, I would like to thank Pieter and Rinus for the fruitful discussions, Ralf for inspiring me to write a functional pearl, and the anonymous reviewers for their valuable and honest comments. -%\appendix \begin{subappendices} \section{Data types and definitions}% \label{sec:cde:appendix} diff --git a/domain-specific_languages/dsl_techniques.tex b/dsl/dsl_techniques.tex similarity index 97% rename from domain-specific_languages/dsl_techniques.tex rename to dsl/dsl_techniques.tex index 7ca965f..7791c82 100644 --- a/domain-specific_languages/dsl_techniques.tex +++ b/dsl/dsl_techniques.tex @@ -5,7 +5,8 @@ \pagenumbering{arabic} }{} -\mychapter{chp:dsl_embedding_techniques}{DSL embedding techniques}% +\chapter{DSL embedding techniques}% +\label{chp:dsl_embedding_techniques}% An \gls{EDSL} is a language embedded in a host language created for a specific domain\todo{citation needed?}. \glspl{EDSL} can have one or more backends or views. Commonly used views are pretty printing, compiling, simulating, verifying and proving the program. @@ -100,7 +101,7 @@ If a language construct is added, no compile time guarantee can be given that al | E.e: Eq (Expr e) (Expr e) -> Expr Bool & == e \end{lstClean} -\subsection{Shallow embedding} +\section{Shallow embedding} In a shallowly \gls{EDSL} all language constructs are expressed as functions in the host language. An evaluator view for the example language then can be implemented as the code shown in Definition~\ref{lst:exshallow}. Note that much of the internals of the language can be hidden using monads. @@ -131,13 +132,10 @@ It is nearly impossible to add views to a shallowly embedded language. The only way of achieving this is by reimplementing all functions so that they run all backends at the same time. This will mean that every component will have to implement all views rendering it slow for multiple views and complex to implement. -\subsection{Generalised algebraic data types} - -\section{Shallow embedding} - \subsection{Tagless-final embedding}\label{ssec:tagless} \section{Comparison} +\todo{cite compositional DSLs} \input{subfilepostamble} \end{document} diff --git a/domain-specific_languages/first-class_datatypes.tex b/dsl/first-class_datatypes.tex similarity index 99% rename from domain-specific_languages/first-class_datatypes.tex rename to dsl/first-class_datatypes.tex index b579445..2f3464c 100644 --- a/domain-specific_languages/first-class_datatypes.tex +++ b/dsl/first-class_datatypes.tex @@ -118,7 +118,7 @@ First a data type representing the semantics is defined. In this case, the print \footnotetext{% In this case a \haskellinline{newtype} is used instead of regular \haskellinline{data} declarations. \haskellinline{newtype}s are special data types only consisting a single constructor with one field to which the type is isomorphic. - During compilation the constructor is completely removed resulting in no overhead~\cite[Sec.~4.2.3]{peyton_jones_haskell_2003}. + During compilation the constructor is completely removed resulting in no overhead~\citep[Sec.~4.2.3]{peyton_jones_haskell_2003}. } Since the language is typed, the printer data type has to have a type variable but it is only used during typing---i.e.\ a phantom type~\citep{leijen_domain_2000}: @@ -262,7 +262,7 @@ Metaprogramming is a special flavour of programming where programs have the abil There are several techniques to facilitate metaprogramming, moreover it has been around for many years now~\citep{lilis_survey_2019}. Even though it has been around for many years, it is considered complex~\citep{sheard_accomplishments_2001}. -\gls{TH} is GHC's de facto metaprogramming system, implemented as a compiler extension together with a library~\citep{sheard_template_2002}\cite[Sec.~6.13.1]{ghc_team_ghc_2021}. +\gls{TH} is GHC's de facto metaprogramming system, implemented as a compiler extension together with a library~\citep{sheard_template_2002}\citep[Sec.~6.13.1]{ghc_team_ghc_2021}. Readers already familiar with \gls{TH} can safely skip this section. \gls{TH} adds four main concepts to the language, na\-me\-ly AST data types, splicing, quasiquotation and reification. @@ -711,7 +711,7 @@ bin = QuasiQuoter { quoteExp = parseBin } Custom quasiquoters allow the \gls{DSL} user to enter fragments verbatim, bypassing the syntax of the host language. Pattern matching in general is not suitable for a custom quasiquoter because it does not really fit in one of the four syntactic categories for which custom quasiquoter support is available. However, a concrete use of pattern matching, interesting enough to be beneficial, but simple enough for a demonstration is the \emph{simple case expression}, a case expression that does not contain nested patterns and is always exhaustive. -They correspond to a multi-way conditional expressions and can thus be converted to \gls{DSL} constructs straightforwardly~\cite[Chp.~4.4]{peyton_jones_implementation_1987}. +They correspond to a multi-way conditional expressions and can thus be converted to \gls{DSL} constructs straightforwardly~\citep[Chp.~4.4]{peyton_jones_implementation_1987}. In contrast to the binary literal quasiquoter example, we do not create the parser by hand. The parser combinator library \emph{parsec} is used instead to ease the creation of the parser~\citep{leijen_parsec_2001}. @@ -796,9 +796,9 @@ In generic programming, types are represented as sums of products and using this For example, Rhiger showed a method for expressing statically typed pattern matching using typed higher-order functions~\citep{rhiger_type-safe_2009}. If not the host language but the \gls{DSL} contains higher order functions, the same technique could be applied to port pattern matching to \glspl{DSL} though using an explicit sums of products representation. -Atkey et al.\ describe embedding pattern matching in a \gls{DSL} by giving patterns an explicit representation in the \gls{DSL} by using pairs, sums and injections~\cite[Section~3.3]{atkey_unembedding_2009}. +Atkey et al.\ describe embedding pattern matching in a \gls{DSL} by giving patterns an explicit representation in the \gls{DSL} by using pairs, sums and injections~\citep[Sec.~3.3]{atkey_unembedding_2009}. -McDonell et al.\ extends on this idea, resulting in a very similar but different solution to ours~\citep{mcdonell_embedded_2021}. +McDonell et al.\ extends on this idea, resulting in a very similar but different solution to ours~\citep{mcdonell_embedded_2022}. They used the technique that Atkey et al.\ showed and applied it to deep embedding using the concrete syntax of the host language. The scaffolding---e.g.\ generating the pairs, sums and injections---for embedding is automated using generics but the required pattern synonyms are generated using \gls{TH}. The key difference to our approach is that we specialise the implementation for each of the backends instead of providing a general implementation of data type handling operations. diff --git a/frontmatter/by.eps b/front/by.eps similarity index 100% rename from frontmatter/by.eps rename to front/by.eps diff --git a/frontmatter/cc.eps b/front/cc.eps similarity index 100% rename from frontmatter/cc.eps rename to front/cc.eps diff --git a/frontmatter/dedication.tex b/front/dedication.tex similarity index 100% rename from frontmatter/dedication.tex rename to front/dedication.tex diff --git a/frontmatter/motto.tex b/front/motto.tex similarity index 100% rename from frontmatter/motto.tex rename to front/motto.tex diff --git a/frontmatter/nd.eps b/front/nd.eps similarity index 100% rename from frontmatter/nd.eps rename to front/nd.eps diff --git a/frontmatter/titlepage.tex b/front/titlepage.tex similarity index 87% rename from frontmatter/titlepage.tex rename to front/titlepage.tex index c5011d5..91dac40 100644 --- a/frontmatter/titlepage.tex +++ b/front/titlepage.tex @@ -94,19 +94,13 @@ { \setlength{\parindent}{0cm}% - Printed by Drukkerij\\ - - Typeset using \LaTeX\\ - - ISBN:\ 111-11-11111-11-1\\ % chktex 8 - - Copyright \copyright{} Mart Lubbers, 2023\\ - - \href{https://martlubbers.net}{martlubbers.net}\\ - + Printed by Drukkerij\\[\baselineskip] + Typeset using \LaTeX\\[\baselineskip] + ISBN:\ 111{-}11{-}11111{-}11{-}1\\[\baselineskip] + Copyright \copyright{} Mart Lubbers, 2023\\[\baselineskip] + \href{https://martlubbers.net}{martlubbers.net}\\[\baselineskip] This work is licensed under the Creative Commons Attribution-NoDerivatives 4.0 International License. - To view a copy of this license, visit \url{http://creativecommons.org/licenses/by-nd/4.0/} or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.\\ - + To view a copy of this license, visit \url{http://creativecommons.org/licenses/by-nd/4.0/} or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.\\[\baselineskip] \includegraphics[scale=.5]{cc} \includegraphics[scale=.5]{by} \includegraphics[scale=.5]{nd} diff --git a/glossaries.tex b/glossaries.tex index f751d78..ff4c8a3 100644 --- a/glossaries.tex +++ b/glossaries.tex @@ -1,9 +1,11 @@ % Acronyms +\newacronym{3COWS}{3COWS}{The three ``CO'' (Composability, Comprehensibility, Correctness) winter school} \newacronym{ADC}{ADC}{analog-to-digital converter} \newacronym{ADT}{ADT}{algebraic data type} \newacronym{API}{API}{application programming interface} \newacronym{ARDSL}{ARDSL}{\gls{ARDUINO} \acrshort{DSL}} \newacronym{BLE}{BLE}{Bluetooth low energy} +\newacronym{CEFP}{CEFP}{central European summer school of functional programming} \newacronym{CRS}{CRS}{\gls{CLEAN} Raspberry Pi system} \newacronym{CRTS}{CRTS}{\gls{CLEAN} Raspberry Pi temperature sensor} \newacronym{CWS}{CWS}{\gls{CLEAN} wemos system} @@ -64,6 +66,10 @@ name=iTask, description={is a \acrshort{TOP} \acrshort{EDSL} for creating distributed multi-user collaborative web applications}, } +\newglossaryentry{TOPHAT}{% + name=TopHat, + description={is a \acrshort{TOP} language designed to formally capture the essence of \gls{TOP}}. +} \newglossaryentry{CLEAN}{% name=Clean, description={Clean \acrlong{LEAN}, a pure lazy functional programming language based on graph rewriting} diff --git a/tiered_vs._tierless_programming/.chktexrc b/intro/.chktexrc similarity index 100% rename from tiered_vs._tierless_programming/.chktexrc rename to intro/.chktexrc diff --git a/intro/hyponymy_of_dsls.tex b/intro/hyponymy_of_dsls.tex new file mode 100644 index 0000000..ce6ceb5 --- /dev/null +++ b/intro/hyponymy_of_dsls.tex @@ -0,0 +1,15 @@ +\documentclass[tikz]{standalone} +\usetikzlibrary{positioning} +\begin{document} +\begin{tikzpicture}[nodes={draw,minimum width=7.5em},node distance=1.5em] + \node (dsl) {domain-specific language}; + \node (sta) [below=of dsl,xshift=-3.75em] {standalone}; + \node (emb) [right=of sta] {embedded}; + \node (het) [below=of emb,xshift=-3.75em] {heterogeneous}; + \node (hom) [right=of het] {homogeneous}; + \draw [->] (dsl) -- (sta); + \draw [->] (dsl) -- (emb); + \draw [->] (emb) -- (het); + \draw [->] (emb) -- (hom); +\end{tikzpicture} +\end{document} diff --git a/introduction/img/orcid.png b/intro/img/orcid.png similarity index 100% rename from introduction/img/orcid.png rename to intro/img/orcid.png diff --git a/introduction/introduction.tex b/intro/introduction.tex similarity index 80% rename from introduction/introduction.tex rename to intro/introduction.tex index 7441014..87aeee6 100644 --- a/introduction/introduction.tex +++ b/intro/introduction.tex @@ -4,7 +4,8 @@ \ifSubfilesClassLoaded{ \pagenumbering{arabic} }{} -\mychapter{chp:introduction}{Introduction} +\chapter{Introduction}% +\label{chp:introduction} %\setlength{\epigraphwidth}{.5\textwidth}% %\epigraphhead[30]{ % A \textbf{rhapsody} in music is a one-movement work that is episodic yet integrated, free-flowing in structure, featuring a range of highly contrasted moods, colour, and tonality. An air of spontaneous inspiration and a sense of improvisation make it freer in form than a set of variations. @@ -12,17 +13,20 @@ % Wikipedia~\citep{wikipedia_contributors_rhapsody_2022} %}% The sheer number of connected devices around us is increasing exponentially. -First and foremost, these devices are driven by software. +All these devices are equipped with processors, sensors, actuators, communication interfaces and much more. +They +The one thing they all have in common +All these connected devices are driven by software and communicate with eachother, sense and interact with the world. This thesis is about \ldots \todo[inline]{introduction} \section{Internet of Things} \todo[inline]{add more citations and rewrite to make modern} The \gls{IOT} is growing rapidly and it is changing the way people and machines interact with the world. -While the term \gls{IOT} briefly gained interest around 1999 to describe the communication of \gls{RFID} devices~\todo{cite}, it probably already popped up halfway the eigthies in a speech by Peter T. Lewis~\citep{peter_t_lewis_speech_1985}. +While the term \gls{IOT} briefly gained interest around 1999 to describe the communication of \gls{RFID} devices~\citep{ashton_internet_1999,ashton_that_2009}, it probably already popped up halfway the eigthies in a speech by \citet{peter_t_lewis_speech_1985}: \begin{quote} - The \acrlong{IOT}, or \acrshort{IOT}, is the integration of people, processes and technology with connectable devices and sensors to enable remote monitoring, status, manipulation and evaluation of trends of such devices. + \emph{The \acrlong{IOT}, or \acrshort{IOT}, is the integration of people, processes and technology with connectable devices and sensors to enable remote monitoring, status, manipulation and evaluation of trends of such devices.} \end{quote} CISCO states that the \gls{IOT} only started when there where as many connected devices as there were people on the globe, i.e.\ around 2008~\citep{evans_internet_2011}. @@ -53,26 +57,23 @@ The number of tiers heavily depends on the complexity of the model but for the i A \gls{SN} is a collection of sensors connected by a mesh network or central hub. \end{description} -While the number of devices seems to be growing exponentially fast, programming \gls{IOT} systems remains difficult as there is a lot of semantic friction~\citep{ireland_classification_2009}. - The devices are a large heterogeneous collection of different platforms, protocols and languages resulting in impedance problems or semantic friction between layers~\citep{ireland_classification_2009}. - -Additionaly, the perception layer often is a heterogeneous collections of microcontrollers as well, each having their own peculiarities, language of choice and hardware interfaces. +Furthermore, the perception layer often is a heterogeneous collections of microcontrollers as well, each having their own peculiarities, language of choice and hardware interfaces. The hardware needs to be cheap, small-scale and energy efficient. As a result, the \glspl{MCU} used to power these devices do not have a lot of computational power, a soup\c{c}on of memory, and little communication bandwidth. Typically the devices do not run a full fledged \gls{OS} but a compiled firmware. This firmware is often written in an imperative language that needs to be flashed to the program memory. -It is possible to dynamically send the program to the program memory using \gls{OTA} programming~\citep{baccelli_scripting_2018,baccelli_reprogramming_2018}. Program memory typically is flash based and only lasts a couple of thousand writes before it wears out. While devices are getting a bit faster, smaller, and cheaper, they keep these properties to an extent. The properties of the device greatly reduce the flexibility for dynamic systems where tasks are created on the fly, executed on demand and require parallel execution. +\todo{\gls{OTA} benoemen?} These problems can be mitigated by dynamically sending code to be interpreted to the \gls{MCU}. With interpretation, a specialized interpreter is flashed in the program memory once that receives the program code to execute at runtime. %weiser_computer_1991 \section{Domain-specific languages} % General -Programming languages can be divided up into two categories: \glspl{DSL} and \glspl{GPL}~\citep{fowler_domain_2010}. +Programming languages can be divided up into two categories: \glspl{DSL}\footnote{Historically this has been called DSEL as well.} and \glspl{GPL}~\citep{fowler_domain_2010}. Where \glspl{GPL} are not made with a demarcated area in mind, \glspl{DSL} are tailor-made for a specific domain. Writing idiomatic domain-specific code in an \gls{DSL} is easy but this may come at the cost of the \gls{DSL} being less expressive to an extent that it may not even be Turing complete. \Glspl{DSL} come in two main flavours: standalone and embedded\footnote{Also called external and internal respectively.} of which \glspl{EDSL} can again be classified into heterogeneous and homogeneous languages (see \cref{fig:hyponymy_of_dsls} for this hyponymy). @@ -80,39 +81,42 @@ Writing idiomatic domain-specific code in an \gls{DSL} is easy but this may come \begin{figure}[ht] \centering \includestandalone{hyponymy_of_dsls} - \caption{Hyponymy of \glspl{DSL} (adapted from~\cite[pg.\ 2]{mernik_extensible_2013})}% + \caption{Hyponymy of \glspl{DSL} (adapted from \cite[pg.\ 2]{mernik_extensible_2013})}% \label{fig:hyponymy_of_dsls} \end{figure} -\glspl{DSL} where historically created as standalone languages which means all the machinery is developed solely for the language. +\subsection{Standalone and embedded} +\glspl{DSL} where historically created as standalone languages, meaning all the machinery is developed solely for the language. The advantage of this approach is that the language designer is free to define the syntax and type system of the language as they wish, not being restricted by any constraint. Unfortunately it also means that they need to develop a compiler or interpreter for the language to be usable making standalone \glspl{DSL} costly to create. Examples of standalone \glspl{DSL} are regular expressions, make, yacc, XML, SQL, \etc. -A radically different approach is embeddding the \gls{DSL} in a host language, i.e.\ \glspl{EDSL}~\citep{hudak_modular_1998}. +A dichotomous approach is embeddding the \gls{DSL} in a host language, i.e.\ \glspl{EDSL}~\citep{hudak_modular_1998}. By defining the language as constructs in the host language, much of the machinery is inherited and the cost of creating embedded languages is very low. +There is more linguistic reuse~\cite{krishnamurthi_linguistic_2001}. There are however two sides to the this coin. If the syntax of the host language is not very flexible, the syntax of the \gls{DSL} may become clumsy. Furthermore, errors shown to the programmer may be larded with host language errors, making it difficult for a non-expert of the host language to work with the \gls{DSL}. +\subsection{Heterogeneity and homogeneity} Tratt applied a notion from metaprogramming~\citep{sheard_accomplishments_2001} to \glspl{EDSL} to define homogeneity and heterogeneity of \glspl{EDSL} as follows~\citep{tratt_domain_2008}: \begin{quote} - a homogeneous system is one where all the components are specifically designed to work with each other, whereas in heterogeneous systems at least one of the components is largely, or completely, ignorant of the existence of the other parts of the system. + \emph{a homogeneous system is one where all the components are specifically designed to work with each other, whereas in heterogeneous systems at least one of the components is largely, or completely, ignorant of the existence of the other parts of the system.} \end{quote} Homogeneous \glspl{EDSL} are therefore languages that are solely defined as an extension to their host language. They often restrict features of the host language to provide a safer interface or capture an idiomatic pattern in the host language for reuse. The difference between a library and a homogeneous \glspl{EDSL} is not always clear. -Examples of homogeneous \glspl{EDSL} are libraries such as ones for \glspl{GUI} creation, LISP's macro system, \etc. +Examples of homogeneous \glspl{EDSL} are libraries such as ones for sets, \glspl{GUI} creation, LISP's macro system, \etc. On the other hand, heterogeneous \glspl{EDSL} are languages that are not executed in the host language. For example, Elliott et al.\ describe the language Pan, for which the final representation in the host language is a compiler that will, when executed, generate code for a completely different target platform~\citep{elliott_compiling_2003}. -In fact, \gls{ITASK} and \gls{MTASK} are both heterogeneous \glspl{EDSL}. +In fact, \gls{ITASK} and \gls{MTASK} are both heterogeneous \glspl{EDSL}\todo{reference next section?}. \section{Task-oriented programming} \Gls{TOP} is a declarative programming paradigm designed to model interactive systems~\citep{plasmeijer_task-oriented_2012}. Instead of dividing problems into layers or tiers, as is done in \gls{IOT} architectures as well, it deals with separation of concerns in a novel way. From the data types, utilising various \emph{type-parametrised} concepts, all other aspects are handled (see \cref{fig:tosd}). -This approach to software development is also called \gls{TOSD}~\citep{wang_maintaining_2018}. +This approach to software development is called \gls{TOSD}~\citep{wang_maintaining_2018}. \begin{figure}[ht] \centering @@ -131,78 +135,72 @@ This approach to software development is also called \gls{TOSD}~\citep{wang_main \end{figure} \begin{description} - \item[Presentation layer: UI] + \item[Presentation layer: \gls{UI}] The \gls{UI} of the system is automatically generated from the representation of the type. - For instance, \gls{TOP} languages implemented in an \gls{FP} language often use generic programming or template metaprogramming to automatically achieve this. - \Gls{TOP} languages embedded in imperative programming languages may use introspection\todo{Do I want this sentence here?}. - Even though the \gls{UI} is generated from the structure of the datatypes, in many practical \gls{TOP} systems it can be tweaked afterwards to suit the specific needs of the application. - \item[Business layer: Tasks] +% For instance, \gls{TOP} languages implemented in an \gls{FP} language often use generic programming or template metaprogramming to automatically achieve this. +% \Gls{TOP} languages embedded in imperative programming languages may use introspection\todo{Do I want this sentence here?}. + Even though the \gls{UI} is generated from the structure of the datatypes, in practical \gls{TOP} systems it can be tweaked afterwards to suit the specific needs of the application. + \item[Business layer: tasks] A task is an abstract representation of a piece of work that needs to be done. It provides an intuitive abstraction over work in the real world. Just as with real-life tasks and workflow, tasks can be combined in various ways such as in parallel or in sequence. - Furthermore, tasks are observable which means it is possible to observe a --- partial --- result during execution and act upon it by for example starting new tasks. + Furthermore, a task is observable which means it is possible to observe a---partial---result during execution and act upon it by for example starting new tasks. Examples of tasks are filling in a form, sending an email, reading a sensor or even doing a physical task. \item[Resource access: \glspl{SDS}] Tasks can communicate using task values but this imposes a problem in many collaboration patterns where tasks that are not necessarily related need to share data. - Tasks can also share data using \glspl{SDS}. - \Glspl{SDS} are an abstraction over any data. + Tasks can also share data using \glspl{SDS}, an abstraction over any data. An \gls{SDS} can represent typed data stored in a file, a chunk of memory, a database \etc. \Glspl{SDS} can also represent external impure data such as the time, random numbers or sensory data. Similar to tasks, transformation and combination of \glspl{SDS} is possible. - \item[\Gls{UOD}: PL] + \item[\Gls{UOD}: programming language] The \gls{UOD} from the business layer is explicitly and separately modelled by the relations that exist in the functions of the host language. - \gls{TOP} languages are usually embedded in \gls{FP} languages but this not necessarily the case. - Some host languages also provide the implementations for the tasks and \glspl{SDS}. - \todo{dit moet beter} \end{description} +The concept of \gls{TOP} originated from the \gls{ITASK} framework, a declarative workflow language for defining multi-user distributed web applications implemented as an \gls{EDSL} in the lazy pure functional programming language \gls{CLEAN}~\citep{plasmeijer_itasks:_2007,plasmeijer_task-oriented_2012}. +While \gls{ITASK} conceived \gls{TOP}, it is not the only \gls{TOP} language. +\Gls{TOPHAT} is a fully formally specified \gls{TOP} language designed to capture the essence of \gls{TOP} formally~\citep{steenvoorden_tophat_2019}. +\citet{piers_task-oriented_2016} created \textmu{}Task, a \gls{TOP} language for specifying non-interruptible embedded systems implemented as an \gls{EDSL} in \gls{HASKELL}. +\citet{van_gemert_task_2022} created LTasks, a \gls{TOP} language for interactive terminal applications implemented in LUA, a dynamically typed imperative language. +\citet{lijnse_toppyt_2022} created Toppyt, a \gls{TOP} language based on \gls{ITASK}, implemented in \gls{PYTHON}, but designed to be simpler and smaller. +Finally there is \gls{MTASK}, \gls{TOP} language designed for defining workflow for \gls{IOT} devices~\cite{koopman_task-based_2018}. +It is written in \gls{CLEAN} as an \gls{EDSL} fully integrated with \gls{ITASK} and allows the programmer to define all layers of an \gls{IOT} system from a single source. + \section{Outline} \todo[inline]{uitbreiden} -%\epigraph{% -% \textbf{rhapsody} /\textipa{['r\ae{}ps@di]}/ \emph{noun} (pl.\ \textbf{-ies}) a piece of music that is full of feeling and is not regular in form: Liszt's Hungarian Rhapsodies. -%}{% -% Oxford Advanced Learners Dictionary~\citep{margaret_deuter_rhapsody_2015}. -%} -This thesis is structured as a purely functional rhapsody. On Wikipedia, a rhapsody is defined as follows~\citep{wikipedia_contributors_rhapsody_2022}: \begin{quote} - A \textbf{rhapsody} in music is a one-movement work that is episodic yet integrated, free-flowing in structure, featuring a range of highly contrasted moods, colour, and tonality. An air of spontaneous inspiration and a sense of improvisation make it freer in form than a set of variations. + \emph{A \textbf{rhapsody} in music is a one-movement work that is episodic yet integrated, free-flowing in structure, featuring a range of highly contrasted moods, colour, and tonality. An air of spontaneous inspiration and a sense of improvisation make it freer in form than a set of variations.} \end{quote} -This thesis follows the tradition and consists of three movements that are episodic yet integrated. +This thesis follows the tradition and consists of three movements that are episodic yet integrated, a purely functional rhapsody. \Cref{prt:dsl} is about \gls{EDSL} techniques, \cref{prt:top} elaborates on \gls{TOP} for the \gls{IOT} and \cref{prt:tvt} compares traditional tiered \gls{IOT} architectures to a tierless architectures such as \gls{TOP}. The movements are readable independently if the reader is familiarised with the background material provided in \cref{chp:introduction}. The thesis wraps up with \cref{chp:conclusion} that provides a conclusion and an outlook on future work. -\subsection{\Cref{prt:dsl}: Domain-specific languages} +\subsection*{\nameref{prt:dsl}} This movement is a cumulative---paper-based---movement that focusses on techniques for embedding \glspl{DSL} in functional programming lanugages. After reading the first chapter, subsequent chapters in this movement are readable as independently. -\subsubsection{\Cref{chp:dsl_embedding_techniques}} +\subsubsection*{\fullref{chp:dsl_embedding_techniques}} This chapter shows the basic \gls{DSL} embedding techniques and compares the properties of several embedding methods. -This chapter is not based on a paper and written as a background for the subsequent chapters in the movement. +This chapter is not based on a paper and written as a extra background material for the subsequent chapters in the movement. -\subsubsection{\Cref{chp:classy_deep_embedding}} +\subsubsection*{\fullref{chp:classy_deep_embedding}} This chapter is based on the paper: \emph{Deep Embedding with Class}~\todo{cite when published}. During a Master's thesis supervision~\citep{amazonas_cabral_de_andrade_developing_2018}, focussing on an early version of \gls{MTASK}, a seed was planted for a novel deep embedding technique for \glspl{DSL} where the resulting language is extendible both in constructs and in interpretation using type classes and existential data types. Slowly the ideas organically grew to form the technique shown in the paper. The research from this paper and writing the paper was solely performed by me. +\Cref{sec:classy_reprise} was added after publication and contains a (yet) unpublished extension of the embedding technique. -\subsubsection{\Cref{chp:first-class_datatypes}} shows how to inherit data types in \glspl{EDSL} using metaprogramming. +\subsubsection*{\fullref{chp:first-class_datatypes}} This chapter is based on the paper: \emph{First-Class Data Types in Shallow Embedded Domain-Specific Languages using Metaprogramming}~\todo{cite when accepted}. +It shows how to inherit data types in \glspl{EDSL} using metaprogramming. The research in this paper 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. -% -%\subsubsection{\Cref{chp:strongly-typed_multi-view_stack-based_computations}} shows how to use advanced \gls{DSL} techniques to embed type safe stack-based computations in a host language. -%This chapter is based on the paper: \emph{Strongly-Typed Multi-View Stack-Based Computations}~\todo{cite when accepted}. -%\todo{Zal ik dit paper wel opnemen? Aangezien Pieter het grotendeels gedaan heeft?} -% -%I supported Pieter Koopman in performing the research in this paper by writing some of the software. -%The paper was mostly written by Pieter Koopman\todo{probably will be}. - -\subsection{\Cref{prt:top}: Task-oriented \texorpdfstring{\acrlong{IOT}}{internet of things} programming } + +\subsection*{\nameref{prt:top}} This part is a monograph focussing on \glspl{TOP} for the \gls{IOT}. Therefore, the chapters depend on eachother and are best read in order. The monograph is compiled from the following papers and revised lecture notes. @@ -281,7 +279,7 @@ The movement is made up out of the following chapters \item TOP for IoT beyond microprocessors \end{itemize} -\subsection{\Cref{prt:tvt}: Tiered versus tierless programming} +\subsection*{\nameref{prt:tvt}} These chapters focus on comparing traditional tiered architectures to tierless architectures and are based on a single journal paper that extended on a conference paper. The conference paper was partly funded by the Radboud-Glasgow Collaboration Fund. It does both a qualitative and a quantitative four-way comparison of a smart campus application. diff --git a/introduction/iot-layers.tex b/intro/iot-layers.tex similarity index 100% rename from introduction/iot-layers.tex rename to intro/iot-layers.tex diff --git a/introduction/tosd.tex b/intro/tosd.tex similarity index 100% rename from introduction/tosd.tex rename to intro/tosd.tex diff --git a/introduction/traditional.tex b/intro/traditional.tex similarity index 100% rename from introduction/traditional.tex rename to intro/traditional.tex diff --git a/lstlangarduino.sty b/lstlangarduino.sty index fb90016..f217c2f 100644 --- a/lstlangarduino.sty +++ b/lstlangarduino.sty @@ -4,5 +4,5 @@ D0,D1,D2,D3,D4,D5,D6,D7,D7,D8,D9,D10,D11,D12,D13,A0,A1,A2,A3,A4,A5,A6,A7,% pinMode,digitalWrite,digitalRead,delay,millis,% }, - basewidth=.6em, + columns=fullflexible, } diff --git a/lstlanghaskell.sty b/lstlanghaskell.sty index 43ed0f4..0abc301 100644 --- a/lstlanghaskell.sty +++ b/lstlanghaskell.sty @@ -1,57 +1,5 @@ -\lstdefinestyle{haskell}{% - language=Haskell, - deletekeywords={% - Applicative,% - Bool,% - Char,% - Double,% - Either,% - Eq,% - False,% - Float,% - Fractional,% - Functor,% - Int,% - Integer,% - Just,% - Left,% - List,% - Maybe,% - Monad,% - Nothing,% - Num,% - Right,% - Show,% - String,% - True,% - concatMap,% - const,% - div,% - error,% - fail,% - foldM,% - foldl,% - foldr,% - fst,% - id,% - length,% - map,% - mapM,% - pred,% - print,% - product,% - sequence,% - show,% - snd,% - sum,% - uncurry,% - undefined,% - unwords,% - unzip,% - zip,% - zipWith,% - } - morekeywords={forall,Q}, +\lstdefinelanguage[Regular]{Haskell}[]{Haskell}{% + keywords={abstype,if,then,else,case,class,data,default,deriving,hiding,if,in,infix,infixl,infixr,import,instance,let,module,newtype,of,qualified,type,where,do,Q}, literate=% {forall}{{$\forall$}}1 {\_}{{\raisebox{.15ex}{\_}}}1 @@ -71,3 +19,132 @@ {\[|}{{$\llbracket$}}1 {|\]}{{$\rrbracket$}}1 } +\lstdefinelanguage[Lhs2Tex]{Haskell}[Regular]{Haskell}{% + moreliterate=% + {e1}{{e\textsubscript{1}}}2 + {e2}{{e\textsubscript{2}}}2 + {e3}{{e\textsubscript{3}}}2 + {e1p}{{e\textsubscript{1}\textsuperscript{$\prime$}}}2 + {e2p}{{e\textsubscript{2}\textsuperscript{$\prime$}}}2 + {eval_0}{{eval\textsubscript{0}}}5 + {Eval_0}{{Eval\textsubscript{0}}}5 + {Expr_0}{{Expr\textsubscript{0}}}5 + {Lit_0}{{Lit\textsubscript{0}}}4 + {Add_0}{{Add\textsubscript{0}}}4 + {Sub_0}{{Sub\textsubscript{0}}}4 + {Print_0}{{Print\textsubscript{0}}}6 + {print_0}{{print\textsubscript{0}}}6 + {Sem_s}{{Sem\textsubscript{s}}}4 + {lit_s}{{lit\textsubscript{s}}}4 + {add_s}{{add\textsubscript{s}}}4 + {sub_s}{{sub\textsubscript{s}}}4 + {Expr_t}{{Expr\textsubscript{t}}}5 + {Eval_t}{{Eval\textsubscript{t}}}5 + {E_t}{{E\textsubscript{t}}}2 + {Printer_t}{{Printer\textsubscript{t}}}8 + {P_t}{{P\textsubscript{t}}}2 + {lit_t}{{lit\textsubscript{t}}}4 + {add_t}{{add\textsubscript{t}}}4 + {Sub_t}{{Sub\textsubscript{t}}}4 + {sub_t}{{sub\textsubscript{t}}}4 + {eval_1}{{eval\textsubscript{1}}}5 + {Eval_1}{{Eval\textsubscript{1}}}5 + {Expr_1}{{Expr\textsubscript{1}}}5 + {Lit_1}{{Lit\textsubscript{1}}}4 + {Add_1}{{Add\textsubscript{1}}}4 + {Sub_1}{{Sub\textsubscript{1}}}4 + {Print_1}{{Print\textsubscript{1}}}6 + {print_1}{{print\textsubscript{1}}}6 + {eval_2}{{eval\textsubscript{2}}}5 + {Eval_2}{{Eval\textsubscript{2}}}5 + {Expr_2}{{Expr\textsubscript{2}}}5 + {Lit_2}{{Lit\textsubscript{2}}}4 + {Add_2}{{Add\textsubscript{2}}}4 + {Sub_2}{{Sub\textsubscript{2}}}4 + {sub_2}{{sub\textsubscript{2}}}4 + {Ext_2}{{Ext\textsubscript{2}}}4 + {Print_2}{{Print\textsubscript{2}}}6 + {print_2}{{print\textsubscript{2}}}6 + {Semantics_2}{{Semantics\textsubscript{2}}}{10} + {eval_3}{{eval\textsubscript{3}}}5 + {Eval_3}{{Eval\textsubscript{3}}}5 + {Print_3}{{Print\textsubscript{3}}}6 + {print_3}{{print\textsubscript{3}}}6 + {opt_3}{{opt\textsubscript{3}}}4 + {Opt_3}{{Opt\textsubscript{3}}}4 + {HasEval_3}{{HasEval\textsubscript{3}}}8 + {getEval_3}{{getEval\textsubscript{3}}}8 + {EvalDict_3}{{EvalDict\textsubscript{3}}}9 + {HasPrint_3}{{HasPrint\textsubscript{3}}}9 + {getPrint_3}{{getPrint\textsubscript{3}}}9 + {PrintDict_3}{{PrintDict\textsubscript{3}}}{10} + {HasOpt_3}{{HasOpt\textsubscript{3}}}7 + {getOpt_3}{{getOpt\textsubscript{3}}}7 + {OptDict_3}{{OptDict\textsubscript{3}}}8 + {Expr_3}{{Expr\textsubscript{3}}}5 + {Lit_3}{{Lit\textsubscript{3}}}4 + {Add_3}{{Add\textsubscript{3}}}4 + {Sub_3}{{Sub\textsubscript{3}}}4 + {sub_3}{{sub\textsubscript{3}}}4 + {Ext_3}{{Ext\textsubscript{3}}}4 + {eval_4}{{eval\textsubscript{4}}}5 + {Eval_4}{{Eval\textsubscript{4}}}5 + {Print_4}{{Print\textsubscript{4}}}5 + {print_4}{{print\textsubscript{4}}}5 + {opt_4}{{opt\textsubscript{4}}}4 + {Opt_4}{{Opt\textsubscript{4}}}4 + {HasEval_4}{{HasEval\textsubscript{4}}}8 + {getEval_4}{{getEval\textsubscript{4}}}8 + {EvalDict_4}{{EvalDict\textsubscript{4}}}9 + {HasPrint_4}{{HasPrint\textsubscript{4}}}9 + {getPrint_4}{{getPrint\textsubscript{4}}}9 + {PrintDict_4}{{PrintDict\textsubscript{4}}}{10} + {HasOpt_4}{{HasOpt\textsubscript{4}}}7 + {getOpt_4}{{getOpt\textsubscript{4}}}7 + {OptDict_4}{{OptDict\textsubscript{4}}}8 + {OptPrintDict_4}{{OptPrintDict\textsubscript{4}}}{14} + {OPD_4}{{OPD\textsubscript{4}}}4 + {Expr_4}{{Expr\textsubscript{4}}}5 + {Lit_4}{{Lit\textsubscript{4}}}4 + {Add_4}{{Add\textsubscript{4}}}4 + {Sub_4}{{Sub\textsubscript{4}}}4 + {SubLoop_4}{{SubLoop\textsubscript{4}}}8 + {sub_4}{{sub\textsubscript{4}}}4 + {Neg_4}{{Neg\textsubscript{4}}}4 + {NegLoop_4}{{NegLoop\textsubscript{4}}}8 + {neg_4}{{neg\textsubscript{4}}}4 + {Ext_4}{{Ext\textsubscript{4}}}4 + {eval_g}{{eval\textsubscript{g}}}5 + {Eval_g}{{Eval\textsubscript{g}}}5 + {Print_g}{{Print\textsubscript{g}}}6 + {print_g}{{print\textsubscript{g}}}6 + {opt_g}{{opt\textsubscript{g}}}4 + {Opt_g}{{Opt\textsubscript{g}}}4 + {HasEval_g}{{HasEval\textsubscript{g}}}8 + {getEval_g}{{getEval\textsubscript{g}}}8 + {EvalDict_g}{{EvalDict\textsubscript{g}}}9 + {HasPrint_g}{{HasPrint\textsubscript{g}}}9 + {getPrint_g}{{getPrint\textsubscript{g}}}9 + {PrintDict_g}{{PrintDict\textsubscript{g}}}{10} + {HasOpt_g}{{HasOpt\textsubscript{g}}}7 + {getOpt_g}{{getOpt\textsubscript{g}}}7 + {OptDict_g}{{OptDict\textsubscript{g}}}8 + {OptPrintDict_g}{{OptPrintDict\textsubscript{g}}}{14} + {OPD_g}{{OPD\textsubscript{g}}}4 + {Expr_g}{{Expr\textsubscript{g}}}5 + {Lit_g}{{Lit\textsubscript{g}}}4 + {Add_g}{{Add\textsubscript{g}}}4 + {Sub_g}{{Sub\textsubscript{g}}}4 + {SubLoop_g}{{SubLoop\textsubscript{g}}}8 + {sub_g}{{sub\textsubscript{g}}}4 + {Neg_g}{{Neg\textsubscript{g}}}4 + {NegLoop_g}{{NegLoop\textsubscript{g}}}8 + {neg_g}{{neg\textsubscript{g}}}4 + {NotLoop_g}{{NotLoop\textsubscript{g}}}8 + {Not_g}{{Not\textsubscript{g}}}4 + {not_g}{{not\textsubscript{g}}}4 + {EqLoop_g}{{EqLoop\textsubscript{g}}}7 + {Eq_g}{{Eq\textsubscript{g}}}3 + {eq_g}{{eq\textsubscript{g}}}3 + {Ext_g}{{Ext\textsubscript{g}}}4 +} diff --git a/lstlanghaskelllhstex.sty b/lstlanghaskelllhstex.sty deleted file mode 100644 index 0239d37..0000000 --- a/lstlanghaskelllhstex.sty +++ /dev/null @@ -1,146 +0,0 @@ -\lstdefinestyle{haskelllhstex}{% - language=Haskell, - deletekeywords={% - True,False,% - Bool,Int,Integer,Float,Double,String,% - Maybe,Nothing,Just,% - zip,length,Show,show,Num,Eq,print,% - error,id}, - morekeywords={forall}, - literate=% - {forall}{{$\forall$}}1 - {\_}{{\raisebox{.15ex}{\_}}}1 - {~}{{\raisebox{-.6ex}{\textasciitilde}}}1 - {\\}{{$\lambda\:$}}1 - {->}{{$\shortrightarrow$}}2 - {<-}{{$\shortleftarrow$}}2 - {=>}{{$\Rightarrow$}}2 - {<=}{{$\Leftarrow$}}2 - {...}{{$\cdots$}}1 %chktex 11 - {e1}{{e\textsubscript{1}}}2 - {e2}{{e\textsubscript{2}}}2 - {e3}{{e\textsubscript{3}}}2 - {e1p}{{e\textsubscript{1}\textsuperscript{$\prime$}}}2 - {e2p}{{e\textsubscript{2}\textsuperscript{$\prime$}}}2 - {eval_0}{{eval\textsubscript{0}}}5 - {Eval_0}{{Eval\textsubscript{0}}}5 - {Expr_0}{{Expr\textsubscript{0}}}5 - {Lit_0}{{Lit\textsubscript{0}}}4 - {Add_0}{{Add\textsubscript{0}}}4 - {Sub_0}{{Sub\textsubscript{0}}}4 - {Print_0}{{Print\textsubscript{0}}}6 - {print_0}{{print\textsubscript{0}}}6 - {Sem_s}{{Sem\textsubscript{s}}}4 - {lit_s}{{lit\textsubscript{s}}}4 - {add_s}{{add\textsubscript{s}}}4 - {sub_s}{{sub\textsubscript{s}}}4 - {Expr_t}{{Expr\textsubscript{t}}}5 - {Eval_t}{{Eval\textsubscript{t}}}5 - {E_t}{{E\textsubscript{t}}}2 - {Printer_t}{{Printer\textsubscript{t}}}8 - {P_t}{{P\textsubscript{t}}}2 - {lit_t}{{lit\textsubscript{t}}}4 - {add_t}{{add\textsubscript{t}}}4 - {Sub_t}{{Sub\textsubscript{t}}}4 - {sub_t}{{sub\textsubscript{t}}}4 - {eval_1}{{eval\textsubscript{1}}}5 - {Eval_1}{{Eval\textsubscript{1}}}5 - {Expr_1}{{Expr\textsubscript{1}}}5 - {Lit_1}{{Lit\textsubscript{1}}}4 - {Add_1}{{Add\textsubscript{1}}}4 - {Sub_1}{{Sub\textsubscript{1}}}4 - {Print_1}{{Print\textsubscript{1}}}6 - {print_1}{{print\textsubscript{1}}}6 - {eval_2}{{eval\textsubscript{2}}}5 - {Eval_2}{{Eval\textsubscript{2}}}5 - {Expr_2}{{Expr\textsubscript{2}}}5 - {Lit_2}{{Lit\textsubscript{2}}}4 - {Add_2}{{Add\textsubscript{2}}}4 - {Sub_2}{{Sub\textsubscript{2}}}4 - {sub_2}{{sub\textsubscript{2}}}4 - {Ext_2}{{Ext\textsubscript{2}}}4 - {Print_2}{{Print\textsubscript{2}}}6 - {print_2}{{print\textsubscript{2}}}6 - {Semantics_2}{{Semantics\textsubscript{2}}}{10} - {eval_3}{{eval\textsubscript{3}}}5 - {Eval_3}{{Eval\textsubscript{3}}}5 - {Print_3}{{Print\textsubscript{3}}}6 - {print_3}{{print\textsubscript{3}}}6 - {opt_3}{{opt\textsubscript{3}}}4 - {Opt_3}{{Opt\textsubscript{3}}}4 - {HasEval_3}{{HasEval\textsubscript{3}}}8 - {getEval_3}{{getEval\textsubscript{3}}}8 - {EvalDict_3}{{EvalDict\textsubscript{3}}}9 - {HasPrint_3}{{HasPrint\textsubscript{3}}}9 - {getPrint_3}{{getPrint\textsubscript{3}}}9 - {PrintDict_3}{{PrintDict\textsubscript{3}}}{10} - {HasOpt_3}{{HasOpt\textsubscript{3}}}7 - {getOpt_3}{{getOpt\textsubscript{3}}}7 - {OptDict_3}{{OptDict\textsubscript{3}}}8 - {Expr_3}{{Expr\textsubscript{3}}}5 - {Lit_3}{{Lit\textsubscript{3}}}4 - {Add_3}{{Add\textsubscript{3}}}4 - {Sub_3}{{Sub\textsubscript{3}}}4 - {sub_3}{{sub\textsubscript{3}}}4 - {Ext_3}{{Ext\textsubscript{3}}}4 - {eval_4}{{eval\textsubscript{4}}}5 - {Eval_4}{{Eval\textsubscript{4}}}5 - {Print_4}{{Print\textsubscript{4}}}5 - {print_4}{{print\textsubscript{4}}}5 - {opt_4}{{opt\textsubscript{4}}}4 - {Opt_4}{{Opt\textsubscript{4}}}4 - {HasEval_4}{{HasEval\textsubscript{4}}}8 - {getEval_4}{{getEval\textsubscript{4}}}8 - {EvalDict_4}{{EvalDict\textsubscript{4}}}9 - {HasPrint_4}{{HasPrint\textsubscript{4}}}9 - {getPrint_4}{{getPrint\textsubscript{4}}}9 - {PrintDict_4}{{PrintDict\textsubscript{4}}}{10} - {HasOpt_4}{{HasOpt\textsubscript{4}}}7 - {getOpt_4}{{getOpt\textsubscript{4}}}7 - {OptDict_4}{{OptDict\textsubscript{4}}}8 - {OptPrintDict_4}{{OptPrintDict\textsubscript{4}}}{14} - {OPD_4}{{OPD\textsubscript{4}}}4 - {Expr_4}{{Expr\textsubscript{4}}}5 - {Lit_4}{{Lit\textsubscript{4}}}4 - {Add_4}{{Add\textsubscript{4}}}4 - {Sub_4}{{Sub\textsubscript{4}}}4 - {SubLoop_4}{{SubLoop\textsubscript{4}}}8 - {sub_4}{{sub\textsubscript{4}}}4 - {Neg_4}{{Neg\textsubscript{4}}}4 - {NegLoop_4}{{NegLoop\textsubscript{4}}}8 - {neg_4}{{neg\textsubscript{4}}}4 - {Ext_4}{{Ext\textsubscript{4}}}4 - {eval_g}{{eval\textsubscript{g}}}5 - {Eval_g}{{Eval\textsubscript{g}}}5 - {Print_g}{{Print\textsubscript{g}}}6 - {print_g}{{print\textsubscript{g}}}6 - {opt_g}{{opt\textsubscript{g}}}4 - {Opt_g}{{Opt\textsubscript{g}}}4 - {HasEval_g}{{HasEval\textsubscript{g}}}8 - {getEval_g}{{getEval\textsubscript{g}}}8 - {EvalDict_g}{{EvalDict\textsubscript{g}}}9 - {HasPrint_g}{{HasPrint\textsubscript{g}}}9 - {getPrint_g}{{getPrint\textsubscript{g}}}9 - {PrintDict_g}{{PrintDict\textsubscript{g}}}{10} - {HasOpt_g}{{HasOpt\textsubscript{g}}}7 - {getOpt_g}{{getOpt\textsubscript{g}}}7 - {OptDict_g}{{OptDict\textsubscript{g}}}8 - {OptPrintDict_g}{{OptPrintDict\textsubscript{g}}}{14} - {OPD_g}{{OPD\textsubscript{g}}}4 - {Expr_g}{{Expr\textsubscript{g}}}5 - {Lit_g}{{Lit\textsubscript{g}}}4 - {Add_g}{{Add\textsubscript{g}}}4 - {Sub_g}{{Sub\textsubscript{g}}}4 - {SubLoop_g}{{SubLoop\textsubscript{g}}}8 - {sub_g}{{sub\textsubscript{g}}}4 - {Neg_g}{{Neg\textsubscript{g}}}4 - {NegLoop_g}{{NegLoop\textsubscript{g}}}8 - {neg_g}{{neg\textsubscript{g}}}4 - {NotLoop_g}{{NotLoop\textsubscript{g}}}8 - {Not_g}{{Not\textsubscript{g}}}4 - {not_g}{{not\textsubscript{g}}}4 - {EqLoop_g}{{EqLoop\textsubscript{g}}}7 - {Eq_g}{{Eq\textsubscript{g}}}3 - {eq_g}{{eq\textsubscript{g}}}3 - {Ext_g}{{Ext\textsubscript{g}}}4 -} diff --git a/other.bib b/other.bib index ba881fd..188e95c 100644 --- a/other.bib +++ b/other.bib @@ -287,6 +287,22 @@ Publisher: Association for Computing Machinery}, file = {Mitchell and Plotkin - 1988 - Abstract types have existential type.pdf:/home/mrl/.local/share/zotero/storage/QXDE5H7C/Mitchell and Plotkin - 1988 - Abstract types have existential type.pdf:application/pdf}, } +@inproceedings{steenvoorden_tophat_2019, + address = {New York, NY, USA}, + series = {{PPDP} '19}, + title = {{TopHat}: {A} {Formal} {Foundation} for {Task}-{Oriented} {Programming}}, + isbn = {978-1-4503-7249-7}, + url = {https://doi.org/10.1145/3354166.3354182}, + doi = {10.1145/3354166.3354182}, + abstract = {Software that models how people work is omnipresent in today's society. Current languages and frameworks often focus on usability by non-programmers, sacrificing flexibility and high level abstraction. Task-oriented programming (TOP) is a programming paradigm that aims to provide the desired level of abstraction while still being expressive enough to describe real world collaboration. It prescribes a declarative programming style to specify multi-user workflows. Workflows can be higher-order. They communicate through typed values on a local and global level. Such specifications can be turned into interactive applications for different platforms, supporting collaboration during execution. TOP has been around for more than a decade, in the forms of iTasks and mTasks, which are tailored for real-world usability. So far, it has not been given a formalisation which is suitable for formal reasoning.In this paper we give a description of the TOP paradigm and then decompose its rich features into elementary language elements, which makes them suitable for formal treatment. We use the simply typed lambda-calculus, extended with pairs and references, as a base language. On top of this language, we develop TopHat, a language for modular interactive workflows. We describe TopHat by means of a layered semantics. These layers consist of multiple big-step evaluations on expressions, and two labelled transition systems, handling user inputs.With TopHat we prepare a way to formally reason about TOP languages and programs. This approach allows for comparison with other work in the field. We have implemented the semantic rules of TopHat in Haskell, and the task layer on top of the iTasks framework. This shows that our approach is feasible, and lets us demonstrate the concepts by means of illustrative case studies. TOP has been applied in projects with the Dutch coast guard, tax office, and navy. Our work matters because formal program verification is important for mission-critical software, especially for systems with concurrency.}, + booktitle = {Proceedings of the 21st {International} {Symposium} on {Principles} and {Practice} of {Declarative} {Programming}}, + publisher = {Association for Computing Machinery}, + author = {Steenvoorden, Tim and Naus, Nico and Klinik, Markus}, + year = {2019}, + note = {event-place: Porto, Portugal}, + file = {Steenvoorden et al. - 2019 - TopHat A Formal Foundation for Task-Oriented Prog.pdf:/home/mrl/.local/share/zotero/storage/W7HJ5MEF/Steenvoorden et al. - 2019 - TopHat A Formal Foundation for Task-Oriented Prog.pdf:application/pdf}, +} + @inproceedings{yorgey_giving_2012, address = {New York, NY, USA}, series = {{TLDI} '12}, @@ -442,6 +458,16 @@ Publisher: Association for Computing Machinery}, file = {Barendsen and Smetsers - 1996 - Uniqueness typing for functional languages with gr.pdf:/home/mrl/.local/share/zotero/storage/BPRC6KJK/Barendsen and Smetsers - 1996 - Uniqueness typing for functional languages with gr.pdf:application/pdf}, } +@inproceedings{lijnse_itasks_2009, + title = {{iTasks} 2: {iTasks} for {End}-users}, + booktitle = {International {Symposium} on {Implementation} and {Application} of {Functional} {Languages}}, + publisher = {Springer}, + author = {Lijnse, Bas and Plasmeijer, Rinus}, + year = {2009}, + pages = {36--54}, + file = {Lijnse and Plasmeijer - 2009 - iTasks 2 iTasks for End-users.pdf:/home/mrl/.local/share/zotero/storage/KACEWKXY/Lijnse and Plasmeijer - 2009 - iTasks 2 iTasks for End-users.pdf:application/pdf}, +} + @mastersthesis{bohm_asynchronous_2019, address = {Nijmegen}, title = {Asynchronous {Actions} in a {Synchronous} {World}}, @@ -523,6 +549,16 @@ few changes in existing programs.}, file = {Baccelli et al. - 2018 - Reprogramming Low-end IoT Devices from the Cloud.pdf:/home/mrl/.local/share/zotero/storage/M6LX5ZJN/Baccelli et al. - 2018 - Reprogramming Low-end IoT Devices from the Cloud.pdf:application/pdf}, } +@mastersthesis{piers_task-oriented_2016, + address = {Nijmegen}, + title = {Task-{Oriented} {Programming} for developing non-distributed interruptible embedded systems}, + language = {en}, + school = {Radboud University}, + author = {Piers, Jasper}, + year = {2016}, + file = {Piers - Task-Oriented Programming for developing non-distr.pdf:/home/mrl/.local/share/zotero/storage/X8BZM9D4/Piers - Task-Oriented Programming for developing non-distr.pdf:application/pdf}, +} + @inproceedings{baccelli_scripting_2018, title = {Scripting {Over}-{The}-{Air}: {Towards} {Containers} on {Low}-end {Devices} in the {Internet} of {Things}}, booktitle = {{IEEE} {PerCom} 2018}, @@ -566,12 +602,23 @@ few changes in existing programs.}, file = {groj10-Haskell_front_end_Clean.pdf:/home/mrl/.local/share/zotero/storage/WVZWX8WT/groj10-Haskell_front_end_Clean.pdf:application/pdf}, } +@article{plasmeijer_itasks:_2007, + title = {{iTasks}: executable specifications of interactive work flow systems for the web}, + volume = {42}, + number = {9}, + journal = {ACM SIGPLAN Notices}, + author = {Plasmeijer, Rinus and Achten, Peter and Koopman, Pieter}, + year = {2007}, + pages = {141--152}, + file = {plar2007-ICFP07-iTasks.pdf:/home/mrl/.local/share/zotero/storage/N8EUZP7D/plar2007-ICFP07-iTasks.pdf:application/pdf}, +} + @incollection{plasmeijer_shallow_2016, address = {Cham}, series = {Lecture {Notes} in {Computer} {Science}}, title = {A {Shallow} {Embedded} {Type} {Safe} {Extendable} {DSL} for the {Arduino}}, volume = {9547}, - isbn = {978-3-319-39109-0 978-3-319-39110-6}, + isbn = {978-3-319-39110-6}, url = {http://link.springer.com/10.1007/978-3-319-39110-6}, urldate = {2017-02-22}, booktitle = {Trends in {Functional} {Programming}}, @@ -737,7 +784,6 @@ Publisher: Association for Computing Machinery}, journal = {Parallel Processing Letters}, author = {Hammond, Kevin and Berthold, Jost and Loogen, Rita}, year = {2003}, - note = {\_eprint: https://doi.org/10.1142/S0129626403001380}, pages = {413--424}, file = {Hammond et al. - 2003 - AUTOMATIC SKELETONS IN TEMPLATE HASKELL.pdf:/home/mrl/.local/share/zotero/storage/HBQ8UXY3/Hammond et al. - 2003 - AUTOMATIC SKELETONS IN TEMPLATE HASKELL.pdf:application/pdf}, } @@ -918,7 +964,7 @@ Publisher: Association for Computing Machinery}, author = {Shioda, Masato and Iwasaki, Hideya and Sato, Shigeyuki}, year = {2014}, note = {event-place: Västerås, Sweden}, - keywords = {D language, Embedded domain specific languages, Library, Metaprogramming}, + keywords = {Metaprogramming, D language, Embedded domain specific languages, Library}, pages = {63--72}, file = {Shioda et al. - 2014 - LibDSL A Library for Developing Embedded Domain S.pdf:/home/mrl/.local/share/zotero/storage/3WFYJPFR/Shioda et al. - 2014 - LibDSL A Library for Developing Embedded Domain S.pdf:application/pdf}, } @@ -954,7 +1000,7 @@ Publisher: Association for Computing Machinery}, author = {Eisenberg, Richard A. and Stolarek, Jan}, year = {2014}, note = {event-place: Gothenburg, Sweden}, - keywords = {defunctionalization, Haskell, type-level programming}, + keywords = {Haskell, defunctionalization, type-level programming}, pages = {95--106}, file = {Eisenberg and Stolarek - 2014 - Promoting Functions to Type Families in Haskell.pdf:/home/mrl/.local/share/zotero/storage/PQXGBM6M/Eisenberg and Stolarek - 2014 - Promoting Functions to Type Families in Haskell.pdf:application/pdf}, } @@ -972,7 +1018,7 @@ Publisher: Association for Computing Machinery}, author = {Viera, Marcos and Balestrieri, Florent and Pardo, Alberto}, year = {2018}, note = {event-place: Lowell, MA, USA}, - keywords = {Attribute Grammars, Dynamics, EDSL, Haskell, Staging}, + keywords = {Haskell, EDSL, Attribute Grammars, Dynamics, Staging}, pages = {95--106}, file = {Viera et al. - 2018 - A Staged Embedding of Attribute Grammars in Haskel.pdf:/home/mrl/.local/share/zotero/storage/53D4HT9C/Viera et al. - 2018 - A Staged Embedding of Attribute Grammars in Haskel.pdf:application/pdf}, } @@ -1132,7 +1178,7 @@ Publisher: Association for Computing Machinery}, author = {Najd, Shayan and Lindley, Sam and Svenningsson, Josef and Wadler, Philip}, year = {2016}, note = {event-place: St. Petersburg, FL, USA}, - keywords = {domain-specific language, DSL, EDSL, embedded language, normalisation, QDSL, quotation, subformula principle}, + keywords = {EDSL, domain-specific language, DSL, embedded language, normalisation, QDSL, quotation, subformula principle}, pages = {25--36}, file = {Najd et al. - 2016 - Everything Old is New Again Quoted Domain-Specifi.pdf:/home/mrl/.local/share/zotero/storage/NZJW5ZVF/Najd et al. - 2016 - Everything Old is New Again Quoted Domain-Specifi.pdf:application/pdf}, } @@ -1248,6 +1294,7 @@ Publisher: Association for Computing Machinery}, author = {{Peter T. Lewis}}, month = sep, year = {1985}, + annote = {By connecting devices such as traffic signal control boxes, underground gas station tanks and home refrigerators to supervisory control systems, modems, auto-dialers and cellular phones, we can transmit status of these devices to cell sites, then pipe that data through the Internet and address it to people near and far that need that information.  I predict that not only humans, but machines and other things will interactively communicate via the Internet.  The Internet of Things, or IoT, is the integration of people, processes and technology with connectable devices and sensors to enable remote monitoring, status, manipulation and evaluation of trends of such devices.  When all these technologies and voluminous amounts of Things are interfaced together -- namely, devices/machines, supervisory controllers, cellular and the Internet, there is nothing we cannot connect to and communicate with.  What I am calling the Internet of Things will be far reaching.}, } @article{weiser_computer_1991, @@ -1405,17 +1452,6 @@ Publisher: Association for Computing Machinery}, file = {Crooijmans - 2021 - Reducing the Power Consumption of IoT Devices in T.pdf:/home/mrl/.local/share/zotero/storage/YIEQ97KK/Crooijmans - 2021 - Reducing the Power Consumption of IoT Devices in T.pdf:application/pdf}, } -@article{mcdonell_embedded_2021, - title = {Embedded {Pattern} {Matching}}, - volume = {abs/2108.13114}, - url = {https://arxiv.org/abs/2108.13114}, - journal = {CoRR}, - author = {McDonell, Trevor L. and Meredith, Joshua D. and Keller, Gabriele}, - year = {2021}, - note = {arXiv: 2108.13114}, - file = {2108.13114.pdf:/home/mrl/.local/share/zotero/storage/AJAT8AXI/2108.13114.pdf:application/pdf}, -} - @misc{wadler_expression_1998, title = {The expression problem}, url = {https://homepages.inf.ed.ac.uk/wadler/papers/expression/expression.txt}, @@ -1425,6 +1461,9 @@ Publisher: Association for Computing Machinery}, month = nov, year = {1998}, note = {e-mail message, accessed on 2021-02-24}, + annote = { + +}, } @misc{margaret_deuter_rhapsody_2015, @@ -1446,6 +1485,7 @@ Publisher: Association for Computing Machinery}, author = {{Wikipedia contributors}}, year = {2022}, note = {accessed on: 2022-09-06}, + annote = {[Online; accessed 6-September-2022]}, } @incollection{backus_introduction_1990, @@ -1563,7 +1603,7 @@ Publisher: Association for Computing Machinery}, author = {de Vries, Edsko and Löh, Andres}, year = {2014}, note = {event-place: Gothenburg, Sweden}, - keywords = {datatype-generic programming, generic views, json, lenses, metadata, sums of products, universes}, + keywords = {lenses, datatype-generic programming, generic views, json, metadata, sums of products, universes}, pages = {83--94}, file = {de Vries and Löh - 2014 - True Sums of Products.pdf:/home/mrl/.local/share/zotero/storage/QHT5IPQA/de Vries and Löh - 2014 - True Sums of Products.pdf:application/pdf}, } @@ -1598,7 +1638,7 @@ Publisher: Association for Computing Machinery}, author = {Pickering, Matthew and Wu, Nicolas and Kiss, Csongor}, year = {2019}, note = {event-place: Berlin, Germany}, - keywords = {implicits, metaprogramming, staging}, + keywords = {metaprogramming, staging, implicits}, pages = {71--84}, file = {Pickering et al. - 2019 - Multi-Stage Programs in Context.pdf:/home/mrl/.local/share/zotero/storage/3EW7FM44/Pickering et al. - 2019 - Multi-Stage Programs in Context.pdf:application/pdf}, } @@ -1607,6 +1647,7 @@ Publisher: Association for Computing Machinery}, title = {A {Specification} for {Typed} {Template} {Haskell}}, volume = {abs/2112.03653}, url = {https://arxiv.org/abs/2112.03653}, + doi = {10.48550/arXiv.2112.03653}, journal = {CoRR}, author = {Pickering, Matthew and Löh, Andres and Wu, Nicolas}, year = {2021}, @@ -1691,6 +1732,74 @@ Publisher: Association for Computing Machinery}, year = {2015}, doi = {10.3990/1.9789036538039}, note = {ISBN: 978-90-365-3803-9}, - keywords = {Digital Circuits, EC Grant Agreement nr.: FP7/248465, EC Grant Agreement nr.: FP7/610686, EWI-23939, FPGA, Functional Programming, Hardware, Haskell, IR-93962, Lambda calculus, METIS-308711, Rewrite Systems}, + keywords = {Haskell, Digital Circuits, EC Grant Agreement nr.: FP7/248465, EC Grant Agreement nr.: FP7/610686, EWI-23939, FPGA, Functional Programming, Hardware, IR-93962, Lambda calculus, METIS-308711, Rewrite Systems}, + annote = {eemcs-eprint-23939 }, file = {Baaij - 2015 - Digital circuit in CλaSH functional specification.pdf:/home/mrl/.local/share/zotero/storage/MYJ33ISL/Baaij - 2015 - Digital circuit in CλaSH functional specification.pdf:application/pdf}, } + +@inproceedings{mcdonell_embedded_2022, + address = {New York, NY, USA}, + series = {Haskell 2022}, + title = {Embedded {Pattern} {Matching}}, + isbn = {978-1-4503-9438-3}, + url = {https://doi.org/10.1145/3546189.3549917}, + doi = {10.1145/3546189.3549917}, + abstract = {Haskell is a popular choice for hosting deeply embedded languages. A recurring challenge for these embeddings is how to seamlessly integrate user defined algebraic data types. In particular, one important, convenient, and expressive feature for creating and inspecting data—pattern matching—is not directly available on embedded terms. We present a novel technique, embedded pattern matching, which enables a natural and user friendly embedding of user defined algebraic data types into the embedded language, and allows programmers to pattern match on terms in the embedded language in much the same way they would in the host language.}, + booktitle = {Proceedings of the 15th {ACM} {SIGPLAN} {International} {Haskell} {Symposium}}, + publisher = {Association for Computing Machinery}, + author = {McDonell, Trevor L. and Meredith, Joshua D. and Keller, Gabriele}, + year = {2022}, + note = {event-place: Ljubljana, Slovenia}, + keywords = {Haskell, algebraic data types, embedded languages, pattern matching}, + pages = {123--136}, + file = {2108.13114.pdf:/home/mrl/.local/share/zotero/storage/AJAT8AXI/2108.13114.pdf:application/pdf}, +} + +@phdthesis{krishnamurthi_linguistic_2001, + address = {Houston, USA}, + type = {{PhD} {Thesis}}, + title = {Linguistic reuse}, + school = {Rice University}, + author = {Krishnamurthi, Shriram}, + year = {2001}, + file = {Krishnamurthi - 2001 - Linguistic reuse.PDF:/home/mrl/.local/share/zotero/storage/LSKHFPIS/Krishnamurthi - 2001 - Linguistic reuse.PDF:application/pdf}, +} + +@misc{ashton_internet_1999, + address = {London, UK}, + type = {Presentation}, + title = {Internet of {Things}}, + author = {Ashton, Kevin}, + year = {1999}, +} + +@article{ashton_that_2009, + title = {That ‘{Internet} of {Things}’ {Thing}}, + volume = {22}, + number = {7}, + journal = {RFID journal}, + author = {Ashton, Kevin}, + year = {2009}, + note = {Publisher: Hauppauge, New York}, + pages = {97--114}, + file = {Ashton - 2009 - That ‘Internet of Things’ Thing.pdf:/home/mrl/.local/share/zotero/storage/SJ33G6VR/Ashton - 2009 - That ‘Internet of Things’ Thing.pdf:application/pdf}, +} + +@phdthesis{van_gemert_task_2022, + address = {Nijmegen}, + type = {Bachelor's {Thesis}}, + title = {Task {Oriented} {Programming} in {LUA}}, + language = {en}, + school = {Radboud University}, + author = {van Gemert, Dante}, + year = {2022}, + file = {van Gemert - 2022 - Task Oriented Programming in LUA.pdf:/home/mrl/.local/share/zotero/storage/UQHAWT83/van Gemert - 2022 - Task Oriented Programming in LUA.pdf:application/pdf}, +} + +@misc{lijnse_toppyt_2022, + title = {Toppyt}, + url = {https://gitlab.com/baslijnse/toppyt}, + urldate = {2022-10-07}, + author = {Lijnse, Bas}, + year = {2022}, +} diff --git a/preamble.tex b/preamble.tex index 94383b2..b285f89 100644 --- a/preamble.tex +++ b/preamble.tex @@ -1,6 +1,8 @@ +% chktex-file 17 +% chktex-file 15 % Fonts \usepackage[utf8]{inputenc} % Input encoding -\usepackage[T1]{fontenc} % Font encoding +\usepackage[T2A,T1]{fontenc} % Font encoding \usepackage{lmodern} % Nicer font \usepackage{microtype} % Better kerning \usepackage{tipa} % IPA symbols @@ -11,6 +13,7 @@ \usepackage{amssymb} % extra math symbols \usepackage{relsize} % \smaller command \usepackage{siunitx} % typeset units +\usepackage{xcolor} % colors \DeclareSIUnit\noop{\relax} \DeclareSIUnit\celcius{{}^{\circ}\kern-\scriptspace\mathsf{C}} %\usepackage{atveryend} % \smaller command @@ -90,16 +93,21 @@ \renewcommand\partname{Movement} % Rename parts to movements (rhapsody uhu) \addto\captionsbritish{\renewcommand{\partname}{Movement}} \usepackage{titlesec} -\makeatletter \titleformat{\part}[block] {\Huge} {\partname~\thepart:} {20pt} {} - -\newenvironment{chapterabstract} - {\begin{quote}} - {\end{quote}} +% This is required to make \nameref work (https://tex.stackexchange.com/questions/211035/problems-with-nameref-not-refering-to-the-correct-label-x-titlesec) +\makeatletter +\let\titlesec@part\part% +\renewcommand{\part}{\@ifstar\part@star\part@nostar} +\def\part@star#1{\NR@gettitle{#1}\titlesec@part*{#1}} +\def\part@nostar{\@ifnextchar[\part@nostar@opt\part@nostar@nopt} % chktex 9 +\def\part@nostar@nopt#1{\NR@gettitle{#1}\titlesec@part{#1}} +\def\part@nostar@opt[#1]#2{\NR@gettitle{#1}\titlesec@part[#1]{#2}} +\makeatother +\newenvironment{chapterabstract}{\begin{quotation}}{\end{quotation}} % Increase the depth for the table of contents \setcounter{secnumdepth}{3} @@ -108,7 +116,7 @@ % (file) structure \usepackage[subpreambles=true]{standalone} % standalone figures -\usepackage{morewrites} % fix no more room for a new \write +%\usepackage{morewrites} % fix no more room for a new \write \usepackage{appendix} % subappendices, appendices per chapter % Bibliography @@ -116,17 +124,19 @@ \nobibliography* \usepackage{natbib} % Cite bib entry completely \setlength{\bibsep}{0.0pt} +\def\bibfont{\small} %\bibliographystyle{alpha} \bibliographystyle{abbrvnat} +\apptocmd{\thebibliography}{\raggedright}{}{} % Graphics \usepackage{graphicx} % Images -\graphicspath{{img/},{introduction/img}} -\graphicspath{{img/},{introduction/img},{tiered_vs._tierless_programming/img}} +\graphicspath{{img/},{intro/img},{top/img},{tvt/img}} \usepackage{caption} % subfigures \usepackage{subcaption} \usepackage{rotating} \newcommand{\orcid}[1]{\href{https://orcid.org/#1}{\hspace{1mm}\includegraphics[width=1em]{orcid}\hspace{2mm} https://orcid.org/#1}} +\usepackage{tikz} % Tables \usepackage{booktabs} % Nicer tables @@ -140,6 +150,11 @@ % Fix the algorithm font \renewcommand\AlCapFnt{\normalfont} \usepackage{listings} +% https://tex.stackexchange.com/questions/149056/how-can-i-define-additional-literate-replacements-without-deleting-existing-ones +\makeatletter +\def\addToLiterate#1{\edef\lst@literate{\unexpanded\expandafter{\lst@literate}\unexpanded{#1}}} +\lst@Key{moreliterate}{}{\addToLiterate{#1}} +\makeatother % General listings settings \lstset{% basewidth=0.5em, @@ -148,7 +163,6 @@ breaklines=true, captionpos=b, columns=[c]fixed, -% columns=flexible, commentstyle=\sl, escapeinside={[+}{+]}, % chktex 9 frame=L, @@ -165,14 +179,20 @@ } \usepackage{lstlangclean} \usepackage{lstlanghaskell} -\usepackage{lstlanghaskelllhstex} \usepackage{lstlangarduino} +\lstloadlanguages{% + {[Arduino]C++},% + {c},% + {Python},% + {Clean},% + {[Regular]Haskell}, + {[Lhs2Tex]Haskell}} \newcommand{\cinline}[1]{\lstinline[language=c,basicstyle=\tt,postbreak=]|#1|} \newcommand{\arduinoinline}[1]{\lstinline[language={[Arduino]C++},basicstyle=\tt,postbreak=]|#1|} \newcommand{\pythoninline}[1]{\lstinline[language=Python,basicstyle=\tt,postbreak=]|#1|} \newcommand{\cleaninline}[1]{\lstinline[language=Clean,basicstyle=\tt,postbreak=]|#1|} -\newcommand{\haskellinline}[1]{\lstinline[language=Haskell,style=haskell,basicstyle=\tt,postbreak=]|#1|} -\newcommand{\haskelllhstexinline}[1]{\lstinline[language=Haskell,style=haskelllhstex,basicstyle=\tt,postbreak=]|#1|} +\newcommand{\haskellinline}[1]{\lstinline[language={[Regular]Haskell},basicstyle=\tt,postbreak=]|#1|} +\newcommand{\haskelllhstexinline}[1]{\lstinline[language={[Lhs2Tex]Haskell},basicstyle=\tt,postbreak=]|#1|} %For storing listings in footnotes \newsavebox{\LstBox} % Fix list of listings title @@ -205,13 +225,13 @@ {} \lstnewenvironment{lstHaskell}[1][] {% - \lstset{language=Haskell,style=haskell,#1}% + \lstset{language={[Regular]Haskell},#1}% \renewcommand*{\lstlistingname}{Listing (\gls{HASKELL})} } {} \lstnewenvironment{lstHaskellLhstex}[1][] {% - \lstset{language=Haskell,style=haskelllhstex,#1}% + \lstset{language={[Lhs2Tex]Haskell},#1}% \renewcommand*{\lstlistingname}{Listing (\gls{HASKELL})} } {} @@ -234,9 +254,11 @@ \usepackage[noabbrev]{cleveref} % Easy references \crefname{part}{movement}{movements} \crefname{lstlisting}{listing}{listings} +\usepackage{nameref} % to reference names of chapters +\newcommand{\fullref}[1]{\Cref{#1}: \nameref{#1}} % Glossaries and acronyms -\usepackage[acronym,nonumberlist]{glossaries} +\usepackage[nolangwarn,acronym,nonumberlist]{glossaries} \Addlcwords{of} % Titlecase glossary commands \newcommand{\glst}[1]{\titlecap{\glsentrylong{#1}}} @@ -287,23 +309,14 @@ % Custom commands \newcommand{\GHCmod}[1]{\texttt{#1}} -\newcommand{\requiresGHCmod}[1]{\footnote{Requires \GHCmod{#1} to be enabled.}} +\newcommand{\requiresGHCmod}[2][]{\footnote{Requires \GHCmod{#1} to be enabled.#2}} \newcommand{\etc}{{\fontfamily{cmr}\selectfont{\itshape\/\&c}}} \newcommand{\rdmentry}[6]{#1: #2 (#3): #4. #5.\ \href{https://doi.org/#6}{#6}} \newcommand{\mlubbers}{Lubbers, M.\ (Radboud University)} \newcommand{\pkoopman}{Koopman, dr.\ P.\ (Radboud University)} \newcommand{\rplasmeijer}{Plasmeijer, prof.\ dr.\ ir.\ R.\ (Radboud University)} -\newcommand{\mychapter}[2]{ - \chapter{#2}% - \label{#1}% -} -\newcommand{\myappendix}[2]{ - \chapter{#2}% - \label{#1}% -} -\newcommand{\mybackmatter}[2]{ - \chapter{#2}% - \label{#1}% +\newcommand{\mypart}[3]{ + \part[#2: #3]{#2\\[2ex]\smaller{}#3}% + \label{#1} } -\newcommand{\mypart}[3]{\part[#2: #3]{#2\\[2ex]\smaller{}#3}\label{#1}} diff --git a/process_bib.sh b/process_bib.sh new file mode 100755 index 0000000..f7b3f09 --- /dev/null +++ b/process_bib.sh @@ -0,0 +1,7 @@ +#!/bin/sh +if [ $# -eq 0 ] +then + echo "Usage: $0 FILE.bib [FILE.bib ...]" + exit +fi +sed -i '/url = {https\?:\/\/\(doi.org\|link\.springer\|ieeexplore\|dl\.acm\|services\.igi-global\|arxiv\.org\)/d' "${@}" diff --git a/self.bib b/self.bib index 5a970c3..f98e8d1 100644 --- a/self.bib +++ b/self.bib @@ -57,7 +57,6 @@ 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}, @@ -75,7 +74,6 @@ 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}}, diff --git a/thesis.tex b/thesis.tex index b8ec716..930e1de 100644 --- a/thesis.tex +++ b/thesis.tex @@ -11,12 +11,14 @@ \overfullrule=1mm % Just for the todonotes, can go when it's finished -\usepackage{todonotes} +\usepackage[disable]{todonotes} \setuptodonotes{ backgroundcolor=white, linecolor=black, size=scriptsize, } +%\newcommand{\todo}[2][1]{1} +%\newcommand{\listoftodos}{} % Document info \title{\mytitle{} --- \mysubtitle{}} @@ -31,23 +33,20 @@ \frontmatterfancy% %Titlepage -\subfile{frontmatter/titlepage} +\subfile{front/titlepage} \newpage% % Epigraph/motto -\subfile{frontmatter/motto} +\subfile{front/motto} % Table of contents -%\setcounter{tocdepth}{1} +\setcounter{tocdepth}{1} \tableofcontents -\todo{reduce tocdepth to 1 before finishing} \todo{to reduce the size: make listings font smaller} \newpage% % Dedication -\subfile{frontmatter/dedication} - -\listoftodos% +\subfile{front/dedication} % Reset glossary and thus the acronyms \glsresetall{} @@ -58,43 +57,35 @@ \setcounter{chapter}{-1} % Introduction -\subfile{introduction/introduction} +\subfile{intro/introduction} % DSL -\mypart{prt:dsl}{Prelude}{Domain-Specific Languages}% - -% DSL Techniques -\subfile{domain-specific_languages/dsl_techniques} - -% Deep embedding with class -\subfile{domain-specific_languages/class_deep_embedding} - -% First-class data types -\subfile{domain-specific_languages/first-class_datatypes} - -% Stack computations? -\subfile{domain-specific_languages/strongly-typed_multi-view_stack-based_computations} - -\mypart{prt:top}{Exposition}{Task-Oriented Programming for the Internet of Things}% - -\subfile{mtask/mtask} +\part[Prelude: Domain-Specific Languages]{Prelude:\\[2ex]\smaller{}Domain-Specific Languages}% +\label{prt:dsl} +\subfile{dsl/dsl_techniques} % DSL Techniques +\subfile{dsl/class_deep_embedding} % Deep embedding with class +\subfile{dsl/first-class_datatypes} % First-class data types -\mypart{prt:tvt}{Transformation}{Tiered vs.\ tierless programming}% +\part[Exposition: Task-Oriented Programming]{Exposition:\\[2ex]\smaller{}Task-Oriented Programming for the Internet of Things}% +\label{prt:top} +\subfile{top/mtask} % MTask tutorial and implementation -\subfile{tiered_vs._tierless_programming/smart_campus} +\part[Transformation: Tiered vs.\ Tierless Programming]{Transformation:\\[2ex]\smaller{}Tiered vs.\ Tierless Programming}% +\label{prt:tvt} +\subfile{tvt/tvt} % Could Tierless Languages Reduce IoT Development Grief? \bookmarksetup{startatroot} % descend back out of the previous part \addtocontents{toc}{\bigskip}% Insert some whitespace to make the TOC better -\subfile{conclusion/conclusion} +\subfile{concl/conclusion} % Start appendix \appendix% \label{chp:appendix} \addcontentsline{toc}{part}{Appendix} -\subfile{appendix/clean_for_haskell_programmers} -\subfile{appendix/mtask_aux} -\subfile{appendix/bytecode} +\subfile{appx/clean_for_haskell_programmers} +\subfile{appx/mtask_aux} +\subfile{appx/bytecode} \backmatter% \backmatterfancy% @@ -107,19 +98,19 @@ \bibliography{other,self,tiot} % Summary -\subfile{backmatter/summary} +\subfile{back/summary} % Samenvatting -\subfile{backmatter/samenvatting} +\subfile{back/samenvatting} % Acknowledgements -\subfile{backmatter/acknowledgements} +\subfile{back/acknowledgements} % Research data management -\subfile{backmatter/research_data_management} +\subfile{back/research_data_management} % Curriculum vitæ -\subfile{backmatter/curriculum_vitae} +\subfile{back/curriculum_vitae} % Glossary \addcontentsline{toc}{chapter}{Glossary}% diff --git a/tiered_vs._tierless_programming/img/arch.pdf b/tiered_vs._tierless_programming/img/arch.pdf deleted file mode 100644 index f10462827120987f84566aef7cf0832463577f54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 112741 zcmeFZWl){LvMwAJAp{Segdl<7?(XjH9^8Zb!rk57-JJylcXtiJo#4K{MfSe6cYXKP zx##>iRp-a8qWbN3=9%uEW=;11PeCjvBtlJ3!vs$}b9Ar?PftKcVDsG^o{NiC(9THT z!N!h&OwiQM#>&jmiX5I+&d$cr(ZC2YsAyrPPYYQ~V8zM?nfMDfv#}P^cQ7I#6XIZ? zW1y#FV_>0UV`O7wq@<%Gg?vG9(l&qa(*Kuhn0w#D`MScPYJ4d7cvn`hYf-QQ8`Tx)u z`@f8dm{~eNQbsFc2}zESk%5gNBmgNRYZC`k0(yD|W_lhT0(%EYwyoe@(oZthBDL7< z_RfyT58Zsk#~Jun9m0Z`dD#pAX*QOHwuoO1<3OAMj=#KPuR@4B3w>+oaa-?17(nWx+?C z++cTb2PMA4c7EpcO9^Ro)=4M$equuKZo89Dz@+bX*xU8tzteZ=8l+8G} zrGR$-RtLAI62s~H9i<{gX<6nzn<57)kV0an_@9&L&~&$xS&O@q=zS(18JyrhE94o* z=VtFnpPa-zmVXxEmfxMk%W!Vj3&I0Azp-szjAZNi$q|@>g1f36g$Z%@(*aSJGJAEm_h23gW^fiaMrr5l#lgTgEAU{-@mevm` zVrS2~X?_o{IEvOv?XXfv*JqcSFk+SzmR;RT7tzfmk=AXFt&bC6m7ms)*299l(rgK@ zxLqC5!(yG*jX`V~S1s+LsP=uaP(Q6uT0azC8*l~JN=`*@S*EW^4MA8es`NB2vjmVg zR#yaAMX&}C2U>|PI^|32Un}abpf=*Zf?L}$Jas72x{ntW9NfttK(i41?o4B>?26Xg z0OHKX1iR&3LNciuxqb8f!@}y`>WFR@2poglGOs#fxsXm6d4zgzW25*YiJ79> zmdGNBe%k&kRJw=dH4bLWnQH0R?R{>XtFE|soq@zdZ3VArsjrE!l4=bviMSG3eL@Do zMpd7@Ux{oaAGKxb`;q>gOQxNi>Q8+HfSPQv8*`pbCZ{Z?43Z7m6|#?h00uHLKL8^c zn;(Gby%@9{v>LP?v?J1^WZU z$&&p65@f~x$X!Cedh5xh`VnY0f1x4&=LG=cOCwAY-N9=PsGI%$B-#FN0`mj@N8P{Q zx}U^fw7UPZ0(MmIJ3P7;VTmQYYhXVUNwbiz|!G*iD~KWi!YN&bB)`AN*&oc}Iq_P;XN`H*@{DcNq1do|I? z{oGS^v3l}yQYGtJPPwl z#-=MAa_lVJXB;%VsJ=X2+JJ8lXgfX0Wm}jFGYcxe4B5qf(%jqNvVY`n!Riois&S^+ zsjr{t6uv)EDlk4K?!n$dGYwL&V ze-1KBcM1@`$iZo>IfW0ylhuY>$v=h3l7IDr@B>TV>mB)^kr>PV>WOpmL zm)QxADRsL=i;?rG+aib+1fLGy4oK*TH)xBJ5c@^|cevvM2%NzWgm}ZC`#J-^NO_Q3 z_m1&3xT${z*8mB-T(8O(vv9YJ}Ou04>5_!~i|QZzMt_h9v$Z z`6OK=0Bgdxqy(e@2SPnkfD2(ZDZqp97b(D-kbn%}PpC%*2qw%X1B4U)A_GJd5|9Jp z3H8VU$%NVDfONuNfaaTUuEzf%05*T|AoPXin^rhT`!4_SZ>|GZN<`}Oh;VBslGd4 ze~#Ro+#I9|4U|}L_s=?_Vg#D5bx&L6tT+V>E$U+hX!o{tWArc~&#;}qhcOGXS{kfj zVl}0=qBV*yVa|X2*s7maudRPwR8$#gHQd&1o>pXyv|_%DsRDYxR$XyeN!Ry!WvMuZ z7yNr=(OAI@bn$<^Or2NK375yJkOiUKXt6JGsqDZDfOz(UuPhPKNT{ga_m&S@wY6j z!{5x?K?)6|7A$J6K#CyJv6Y_9)@#LhEliNo=NPVc26cn?dP*D`;2xHhXgKN^r6<=& z_H7g)#gaV;^co2xB$6*T4~N%F|F1;-zY_J|T;Bd$tJB}&_dldW&FSK=y2XxdfTWk; zlbg3G%`<#!hbxFzzOM5QUk=pF7CMvxiKHs;cbj^+9^)D3G@S*QK@7DXh4cZdB}@k3UMF|ypK_{;1ld6sN6aOpYz+1CVT&uLSyyhoW`zr%CKZes)d-xhj4Df0-6PQw5k6Bot-0nS!AEDo$GNX1rW- zP#YdbmvUsKeheSU7Wp$6slpYqz_uu5pkAgHys&wmpS6n(Q3t!1c*}dXt3iR0nXE_Wi`|FvmMv@D z!IocBE8^XdO7PmyeF}MSPjdRA)LVCR99%vwsBjA(>4nRsll+g71xOF$u~8H6Ev2`;-HIJp0b0SfuAYv+j#TO3nn_FSdaT#F#&Lj zHS!np{2*MT{!>Q(NDHc_{Acnyi$tl3)#8a|YroDStG)y0`u)|611*=1Qe)KS`JDOb%H(^qnC-7;`s5p%`^l?f4&>*E+3lQ)ki2; zN5dHI?smhmlrvlvF`(95(kN!O@YT@MpMSDf!a}G8`Zh`Qjk(AB&P{Ze?~%(%QeY?&~+q*&T$|z}2L`k4CSUQK=e0EO%vWB-GI`L67P+ z#_z(c)dsY1&`_s;)Yfgr;T$4M+Ly_D%3X+v?SEk}6}sA% zcg!7#k_a>qR2ujwoV(m_ijMhZpuiNK-d8v-B!=cQc4WvDqQOZc}7)@gBCW2r6(P31j?tWGP5dowS7@$qo<9kY;haK%) z@F`8>a}R}noKzH9kHCrcSEdUlPWNNP=`Z&&^i-4mQSZX_&x~`SCF1C%e)NCrUJA>A zKb?Fs1P8M{Zf9n}z9XEt;wopx$=8=8WLr77vIU4_jWr=$OB{Rg4D)4SkJ()Q* zW1DLBUD&W30hZ|)oh^pe{FL6oV(6l8g%zdJJA^u(Y*VGr2zBcMPK$#a{X;y=p`oTe z^H64fpDlb?Ztz-_tq~fnEA%+4`Ho)TSw5m1a~Ghp)Keqe$TaX#j)tB%czka4W6@F@ z`=aoE(t9DN%MGZaoP4#2&p6R;t)@J=V3=ZC9SR7IPAcSt6)1U-iy*^lG09BZK_ z}6ytXjy@LgD|SCn((O$dx;D>WO*&vD%U< z_rk>rT3Qd%UEui1&E@fxp4)m$V1x0eP}M5b>SJ){b!h)8w?gxJ@q@7#Z?97wuucuu z{l!lE%+-3$J7*e}85~_^^EF^UYy9ju)Bx<#kE5wJPUR)$ws(2$w#s=soY(L~@F9<>kW>9h&)(i;x#+$KG zF`S6cnvEa7zTpK;;YP6%a=K&Ww>~H6-7uw3zOCypf*dt(AFX7;d}YY_^jDsP^&^fb>$8nl@ufU>yLHSHQAt_E7o8=C`vHf+ zprVA0y{|i>E%7OmE!zslG_DiG5o?KOe4aOTy~yS)&0|xBFU!zX)W2=6QiJcVa11=Z z9!w~icz)uYH#S<@-sR<+*#oVT%IMgS4}YhGyLnWRQ)IhViJbGlHSacI-mFVKFGy8$ zGg3x(kC&JJ4ac2+E7o2*#BiSAbQyHf6GY0BhM=1&`RaNnRx+h!n>)$bf@T&rtZg^; zcanh1i=22(&j;SORUTTr9_yq6H5sWFsHfoDVkLHRZvmIgQWx6TD-Ld|n-yP!G%MQYI5=6;n)N_jBUFApVLL{V$wM+oJox z4Dj+DbEaec>421Ce#3ed9uU8D75U8gxVK@od`(09L>pdTGIn0@UDvi#Tn|cnQyRC% zC+6YOn>>eS2lq1_)m0|s7Twqtic@z(Cyl~*>kz6G%OV|+1#U<2Sfs};kBTAienD#yDH{BTc&lIiUqyzIRUh=WEcU z;yG!EPF6t+JvlDowoPZA7e_l%*M(bTDp}N|lIE+}Id$LFbjBFFeit$#9OlzUvaZB% zgO*leU-D%XPftWY-3foKQ{Tx|YO|TY1b*i+uL)k!?(an(u^vmRs5!5*Qu*>> zX(dVnQL^|ZHDT2DQXG%O`lskw*%Q;R{9V~wa_dT$q9@-nOPK>`-;+zKnXd*!g(m`B zg@Sl(B}zv|1C%Z50hMs4>DPEIOBxHV6pBdO(;wMGGZ64);*j%=8et57rKnYw$oi*i z^=YO~r;>ZRr9Y_Uq?R~`?EPNO{Dm2_@wKh`RICio0WibG**X23Y2`a#@^!-e3Pu!j zC6_Z($Z~g)IA}{0?kusxeV(?uX|2=*fhIlk2zzR!aysw2hf+IY9;K?8t18PLH$G^* z^S;jwRr7132;u@i`*(cQwp*ZyWs++67GYLR8sG7|8qMAxxbYKTHK3)@6Ekf=hUpf=ZiEc8aX!aB%7e#w%h`(p2qXc{E9TR?N0GxEowE#Qt4z{rf`j+R^SOXrnCpAf8_1^Ur=oET7u@(-uCqll&r&|Es=* zs3p?xs)lx%VXDYo%;SDqw7N?Fw7}#FsP$cNxYU5DQa+z0@mo0DZGhLYU^jmtsltk; zwTt{BSNUtlO{naG$t2CP?D{-qcaopoAWWyG0!6JxTZjHan8$$CXA$OmQt&1RSyoMj ziAGJunI>e)^S5mb5extE+Zy!V>@bghmGe~J1&sE>YeLauWrnOItu%|Bvf~2mhYxM= zy)++ij6xP2M>L~?D?Zb++y^hZS=PXPCk^&t;J55vR3K{n(Yv_)xu(Fn=BEW>rhs~v zMXhVZtGaA>{@F%{==`(K>#jx87T&ju{N3u*ZUaDv@~@gOW&zWl_*0P#`&?rQqF8+5 zY8d*}(BA%T3)JmWMYnFzF=){n*);31+N--bR%osM&9T9SdEAM5#-*<5TN_mh;-REf zQ!exk9rI&-2l<#XFSpde1(!{Rvf7S2#=uc8aSB4HbT8R;2~GrCn`&{T&9T+SzARpT zz2^0<`gMuoWrB@+h?`7WYv+?ndBCr%8*vz$WMY>F=@* z3@^+TZS1{iyKfc0w=7kZa+fr$Tg;%)=paOTT;8mK(Vc0P3ff>R(#Z^Y zLax;58DPkM_V8adR6*%TEYj9L;uzA1Dv$MLAFquOMHER6KN6ZXYq}K=BgQxEU@OnZM6aAB*v;HTlmrC_?squ ze2oBd#Wqc!XUnzc#p8J|x65o)F?p6J^`pYGtmf;ej|%WB1KI&e;j}dnwga_o>*r<9Iuu%KyP-=OqT3Aj_xgsBz!)-_$5LGNp>-L( zi|vw(dv8^`^gw>dG zm~ywZHjrf6IJ1v-60xz-DF^HjK3uA#0fL@rS~q?`_cS%#T8vi;^~>;yT1IWQoBcc*t=pPAx*czqH_qDEmFE(z^q}@U6Y?yJ zOj|h2XJ0%NPe5D=%_cWrLjM9Fsi zRjrD&hm3#3;YLkx8m&TOBxxq~QRw?8y*E;_+0sASBxigh#dCE?Is%=g&2=qZx5D+{ zh*vOt*air_=^IwETr1R8TYCFvf9C68b-?~foi&2lv0{8U7s|-FEZ<=|YCEy!-9vKf zP^=`@=v5nSSKO^%h#pcXuFK?-aQ4?onptbd`%O~;65HS?I*E^Ux0QB%61`cSTLaZ; z^WI2``fX}(Q;ukxJCi1_vsEQUZY(WbRpoDTt~qCKt|A@amRNz1ub#e=Sr%EJ+&tS& zbXQ&cs5Dw%1vl;KKf-KO?LeSsg~)BRt1frl4jYKQ>Q`&tixM>+-1XcFU?5oeMdjN~ zz)+{bw?-mB=Ku(0w$rO$|2EzO2P5g5&D*{d&gU=_)9p>pXG`|w#)k_w=z(MR?MC9> z(YXZaS5CnL0$36@n8+&NXyCcUJIVxiV0SM`AiH~`FP{kr_24l6@jJe+#MhHW2)e>pC=kP8X8I4 zH@==hqa9%Qg>k*6pOWlQ6Os6yZaW__t8{7FdY@soUKZt7fgTp=4nVWclUB1Anfri} zp#3wkd!74$_y*5DEd9=D3+s$2B9=B}*Jl9((4fUpINsYD%l`Vy^+-2%zap! zkfu5ytW#rw7Q9B_WQmIR+M(<%Kb5jKA|kmyD;RVPqH?yYNV-@JQi^~EO^(qt{geLD zd$+JQuMdJ3U(ihZ`Ic53_67gF`+A0*$FhI7w2NG{4?BNQB5Jfj$fTFMfnvV;%Hp^1 z@wK@yuebOAVuq5<8GThrUR5gw7iktq^OgRon1-k`GiCv=QwHhJnC*pW3luIDzOVgD zg%#3=mshn8nLXoF5eBi@MUlx=&o5R9k1GtVovxjlrzdKb9QU)^S=!tXv}+V0d%6gX zp}qLT2?yz+oQ>~MaN|`i&r*yn&mNZ#Yrcn_L)!Is^j?_{ocxHbkR4*_%wXx@*oEr2 z#}Pp?l3CkHvyfO-o1_ETA;pI_zSsN30=3NsVY^|($21S2;}&5TPr~2~f_KAjYv2qX zu{MeA|90X%NP^%XdnAvCX(xHTYxMtZwSRV*0ad7fFBtW%5nUE<|2AkVNF#0lysD7? ztI`GOzX5hoRgkv)>9s9GvN1AZLBH-kqLx4Xy*;bzt(_$*@RC@kMjCGpTX7<7?3b$0 zqd@``NcRPED(Ikg@pCinuk?du5DTL=sG+;>^qb55f_SH6LXgFcC&*KT*nWMqV$Bj7 zcO@YxX@P7NL{}+L>xwZ(j3t?`=uMqku8X~f-awFwqic!Ohf>TXX1?*|=El_uXJa=F z;jk{LC4749R@Gd~dcB1EJp4Jh--BM<06dYGf64*^+3Gaj+ME#Anf-A??enZ<)5Ym7 z!#?k(&)7oH2JV;03Y@%+rjGfu9Oj&LwHRjN{ANW#A~Ku0B@72rU#rWdZj+CmTsT+s zZE(W~?LCic+^H((nt7OcC4Z-`_SH5`9Q$qXME5OR_!nI+d)#MKD_KqGR|v}QYWyt1Nd{GMaM_mX9JYr;d zjBr)0M!|RYW8bBwzOdXT9}p?vbf$juGFuSRz1V% zxLnX$KA1X7?iW<1JzZH}d!9bC^=~{TTn~s#R3%;-8-tF(x8FAEZ6=%&r1aa$ALxv4 z@l9Q7Y?^i)cnh~#*&gW3^iSf)ZSMnKo-EMs&siE1<2fgZfjiWJ+TE$o^nJ8ApMq^z zL#W9+t&dbmFQ9?@6Uh4ajYU*Rf#sgzIFCH1X+2S`@Vd=1N~5?o9tk6-i8xvbORy$y z_1)u6Y@VK}!OH{2wWWTZbJ3yx?9j~{?d`%!nZc)!$zg^*Jx{O6KX;yZ5DZ+S&?s>PZ`#IYb($lWiCu_ff8FjeUr-Z0Y>=j0 z^-E%JY}AS;9vq9zRUS|j94$hDFDs~fK=cy*a(7;TanF_CjP%hiI>k>5BA|sNj!JY( zU4g1KCI`3JK}DA-Of90%Rrg0%^#PX$X7I8QA@C~TWk*cqM`jrCiY(H$tCFmSUS=Prj~E(45=DLXG&mIh9zY}Es3&o1Fxzct}&0fR=}yb@h? z)bzsY!%mJf1DGn!emQ{M`0L8YLe`u+eplT ztWWVj(oCF2AuyYin5vsh%9p4$1KVd8DR$MTv)H!y^A(eM$z)4&R;*Q3EQ3;d9p#`C zZdV*>&*vAdBi7IHm(iQ93P4E*ARZ3Be%)>!hBHdLW};m8K>ORp)NYL<)wWWvn~Wa| zKl_wl?pJeL<{$72T*&ushcAL?+?a^ARUSSpawgkdQcCG)?eILs+u~8zXFQ@Bv-zZ} z;(#yg&d+3yMQN8QmEcItyF29bM=HU(%F}8K z>g@%cJgob}kSAwxFS|Yt>+*wNTu#5a#r_BoLyGECrii+`ZYwO-wM7V)L;5VZOS3S! z@6#d>t;gAxTg(-*?6EVBeIa3-05sHJ8-cZ&DK1p!3(wkTj*X2z_(y10~*isSGw~**}wlJJ~RA_I;`aS!-#-ZPTvH= z7*;T{w{f&HFtR6rkh38~Yy(9j2LjE%9Bf)8BNqn(T5&4~P~e|W!GAu*wc%-nT^vLe z9bT!~kfoxG1h3R;1$bIfCW2S~^sB}~@XCHxfDo%`MQp4cUU4GK1kC@K6=5Y{{inu8 z!1fQ^D^l9X&`e*z#)UwW4l>BhNKZr0K)}GpK*P?=&dv_;D+n=VWbI&2@XCgUxKV_V zuMHgJ^zHr*LMYV#PzmT?*S|XdX9oVh|C5IPuN2t78gzsN2cdIIni<*?Xuf7jLHpm< z{TqG#AL|759rP`2OkO>iKr;8gIr_I;|AUcDPr%N^!Umy=|3|ECCOQ^owtr`BAA?{$ zl!R}2J&bqP+ngVzO?{$|Nz#z~-=YbizyIdrkLAY5A1_w`FY#vz7DgDuPfT8(${R*l zP8~p|*YiTAfB}Q~7Ngud_dAT;#@Xr`(%0O(rQZ z-`YYgMz`U>W8$!o$`z??&{64ToJeQg&a+H!D;}xDBXIiMOF9In(1!54ZSYPOo9p(E zlPHpN&BnCi>YYXLnHS6_(;eWQF2H?$u_iBQ;ZWHNqdOCzpoe=c4Tc@)dIC&742LX4 zh_8YEe9aYvpIW3HkZQ4;&X2u=5{e5#W0w#aPu`XN*msM%wjMXwY_y%fsfc%l%q2N% zL?yq4FMd(^{Ion=84()fY7d-0I{L;N^+nu8d72xYqqL|f1Ju(ja0Xk3yP1OdMpjcn z_^Y=0ZPionRXhzn_dUrlpn>G+cPxAv@NqCjdP0J9JKC!ks5 zTYt}yCxHhdty)ZLy5G_TOXp|r+t7m>sIy(cY{l zf23NBQTo{6&oR(V?Ktmxn6K0-;gR}c&~Ai5bmZH864x_KtJq5^;b<1j5GC)xN>*2y ze!Z!F!<=+XEzh9yBu35j6Y8$FN*X+|MW`F@>%i zbn@jlI*N|^L$Yvd(?`O|@#5Pv2L@S!h;cMA6NGd4-wT2-DYeLkZFN5}El_W8?z9o4 zgz-yIX8ph(WhhlSCk#9nLrJdwD?W&2D1h2Cs3m!!Wm0$(43+1q8aeD@*m@|#4oROq zpLIT8L_dckLJz{bX8D^>ORPrVXwRNQ@V=}{R>BDzrX$g#n#*YUZv#_2Ih?`CVC#r% zxNeOt;l{je^+=eM#$(Oe2_f<)MWW}IW~!oNMP*aIvFeM){_JB-Wz*E-$JkJ=GDI^E z9LH!+7Oe$&^FMNA19@N2~>^Z$?=X<(BFaVP;htbLslB)-z_Udt{Khkd!jX>EV<-2Avva$9%mb2Jx7yL z;xi~~TLAMacUS{anNdb?IZ2-ITAQo%y1ff+?(tF*#( z@N=BiSSVe=7j{;$d&@Bt!=c?|Q?H8SBoz75yA}y>`Mhk|)x=G(iPBlpoSC7ut>^&x z@7VFx8Qb$OZ~F@zMDY(upD{8H zgC_{=-6xr9R#%CzG#QHJl;lFh20n^t&8nvlm*i;h2?qs9v zY6|bvTDqU0*g;3ko)t3>e^FXcGeXj^d*YFZxz_8;h;)T9Tbd?U*^I}7L$CiW<=de* z2WzO8&>B2ChHI3n;Z-)_R>4L+%6p||PMO0aQxW4eIb4R*u&|;~%CYz~K27<(q{$_$ zR*3cD=W5sCZIgbaM32W3(j^f=cl0Jzzf*_46;q&=}VqkfacAj2ob-f5bibmW4UNK`!F>&_rH^ zwTyaD4jA1NI91N+Pt%;Jv+34?u2$St2|3(MNGoQ(u&8jLYmx}Ip$mGmOjT-?#@`@J z)J#gK!`${6g}o_6;cZH%PIyr#G>^LSA^lla{GTj=NNsjruuZ|O>`&}u&dL51ER;@# ziXZsC*6jrYms%^OB7susLgn6ZvJYoJz;Tbs`4IO}9} zY4DBT@JqEnhXh&6dlaRnBO66gxD%v1&>xewi(X;6w;aRIv7}c&MZ6jt36lxIfv?wks??y-EvxbrD}fjrUeU_Z@L4$(>;>o_=j_t#N&`FZ^}YARqrCu6^iH#ad)l`zl#eX7Ie?vyavmGf|0u`1+ywWN(69d znSS%YImPwHOi2oEfcp{QhZS2y-2{5Z3z@Ik=AFsuo5>E7oRo~E_vP<0q2ND|rjCDK zta|5(bW^!mKi=hW!}yCO`+YO3)I>bg^hPAtjkxDF;Aj%8{GfUi_iambye+~X_ZlzdjU(m!DxW|nhHxYpBmSTCl~Tq4&%{>bOyFTkCrT< zB9;Yq+C}}2ecvjnlobZn)UrH%!JpXiq8C-oC<4o#Ou6#FyNeDdR_~-}5|oxp#8M>y+O;4e|VrLoM)oWarAofVh zDEPjv7QMI-S|`3Sx6G$V|F-)y!G!R$u}e!8R7*RPzz|1K$mt*cc?n-1Oj>)qJKlL91*~OL566&h)N0>*d4#ON|xcbXJgSHq3N_`-UBg z2Zc5<8Xa#r3Gar8gds{wuKSRkm&CZLMrB`6nH{*9wg9>%#y+ki#=ex~H?ldo#EyEz zecP(@Udt6-89@+N)DLgbM?UyPEIFw7VkS+&e0wCv$*5FaKGsJ83yX}stMKtMJDKp* zvg2pRy2HcOOk=xkseP^tzKb9iaXa2Z^NuN(Mx$g&Fz$L$8AlR0!MbN5**$u`XNr5e zbNQ@1f9`gp>}+O3NSH91nJ49& znnBk{M`UdFFpDCJS;bRUfa|cxF#2W5hn>+<#JXvYl~8`Qmj1}hmcEe};#&fKP3VXn zV6|glO*cnRo#+QYo@k>#7ovBoQjTxzHf&=PCS>>YPG(wr_BeNCe^U-J!L?Mq1Agm8 z+Fi>QHC$89go(_h|2=m({#N@ptU@{CPhF^b-e_noyn~`7(S8+Hi?a_q4a4s<;H@|i zo-)xC323{o4Fcv!nXDFQrZlGRVLs3@cK4tALF5mbg6BNzdyElZW0wZV%q0^{bjqu zX9@p>N3NH(2^#thjvQ?CesM2~in3YMPQ9jUn+W^npUIpGiVa%-a zwfEghW>!O5-CBFa4652NDOm((zQJw9nHO)i7yP0vvZ4iBLdAMGW@9%<>2&jnJWvKh zVczq8bmANH8}<0Ic_!#~@x>VS=}3Tt2xSDhLlKz#47%!FN%Ty>O~kA;4%O`p#j?^a?o`8_V*LBt0UvC_=XY#z$XE5_ zW!o(u9GL!mF6J8Wv(9VXJSEEFI*T=tiD7P)J&L?HAaAM9_&8xwoU$I`tD$Rwq^opt z+?dDZH7$V(M7fF@TwXI4#6>0&jhFDuI|RPDuq2t7Ew`_*e}vNiEG?MX6fn__th8W} z>!yU6(Y;I*yo*X5C5)}+jPD!6m3!sknTONRG~Pe2<}9h@m5Z9W&7R368FpB=tjve5 z2Z)eYo#NZk8Qq~Y&8EhO5C~~Xvh*VxF5>L#TR*zJSli`TWyhtdcjceAB!{pt?*Mz1 z>Nj}w$lh%Y+Mmv{G9*59t<$49k zU6%jOhVZH^xJ~tjnVhn8hId2vKJ1aoi$8Cx+^$`Dp$dG6HyU7e5O6w_o~Je=d<=Ft zlc@v|UK>s!tn$7;8_LLQ7}w-gE}Hjbn<<|)*y;GLSxR~K_6c|PT$@!8=Y5MOl+e{9 zFF!Z*C-_CX@t}5tbLUAq1Bv|?gdPoSHM#|1J&|fr07LvYC0l(m><(SkkJM2oP48>F zt54aVkf?Oh(*3l&o^Y#gNa0ov-jifSnSw-*=rw4k>_qP)cJZZe!S%Rc*_9oyvQ@7E zcV#`PQ&^r*IalYZ(}24~Pq{J}k9SYz2UK=ktfAR752Gku?p?B|$BiF4iWF4aBYDn4 z92XiH#Vs~(==Gb>@*R!U4z1DjdF-lOth+jPTrkTZ$4)vMzC$Vb1K{} zS9#!PE)eI|%5GSlu=bX1wl(1H0AU}56EEN+LiXKPh*~%7=iN9wQ08$Got5;4v|POo zoCe9zExOU?wWo+t+p&LwP}i!Q-#V5*c3JEXm3|n@&SI|n-6RMEzf%#pI^pX@R*DZ= zqjX)-aTQe{R+fg&W(^*jRXXO!pXk>@4(5c~N0?_Hrz@?x^7H)aP)6Zw{h_0T;k~UJ zlpj?_ZeH`B)y2lsg+X^_vYqBk+IGe z(Sg&*){|jEg_HwM^7-v{M zP!4f<7FUN?=@_v}lVUw_kIj2FBC+RXJnIxjXkv&+Y0fm+j&lBiS8t8egG_02Qf*V;jCe!gT8ZB*hLE53ful_%?Op@loOHWqvVm@=D{R=` z4iVuSl@~l|$Ev8@=bzws0zxa?pOHo@+N-oE)of`Is>H`|dX6;fV5dE+hnHp)Lcm26 zjip)q&vWWieWylR$C>?E$KV}TiEDQp?6v`0dH06P#ITHnoUBTf?lYWOo?AS?GBD!a z_w9o`q2W~+s&)mmZs?zfcRLp1^FTupm(!r~_*x5$Z32fEF*nb6R%&&f6I|HK+x<2q z2%$xmVr`516X~r7Bjv3b<*jMn**CSV1FUkCDdGz5Q}$@6%3#X_%ou(P;?V)u*alK^ zBY9jF-1@A5ca}UNf?2`g=DxM7=rO#MqXSg;cB+tn(}8^Usg`;-Hn{vkUq)j478;t+ zu9&^JSJ@li)Uu-J48BJr38XhBsgBA{K64V~LMJ*-k2uQAZihJRtVdHwssFNpIt7=z zh&qK%p*-`gh;;D@)6rCTkt2&}T;^`NRH(xV&7qO2(oISt? zit%J=!Qz2>LaZaT9SjYj>);mXQty3zb}WlEL+= zlYE^BEhQkeLxJ~DxP&kzDT3t&wJd1!A_zBH?!!6$JUb+~ByfbJk!iNelIMnNbXTc) z1h6cLZt0&E)fZ6SQ*JU+?lXe;pMxzxnlNqURr_tq7m>i!8?5TjTR=pZ`T_p9p|9&c zJS&*4yn$|@G>s7bYMbg~+jivbUjkQ2X0u}Lq7k$2#4hQtB95gFw4)}b7lrQI+V!$-3TF({Ac<^b!!$Kl(Fbo!=NdnAb)!UmLv~J~;eZQ1 z{K&Az&b!QIOQ9iY$8w*shN^%@ESY_(PurKcPy+wn*Wv>_YFv+71 zdbqo~b(zfAnW1NCmG{PN%sR=oyl?X)I`xa;GER)MzgNK<1_ZMb+8*wnQv0%sayC~n zqERm`<@E|6iEEOXh-;F5b>_6OV*1-|lhUvcc&EsGmWpn5|8-0`YEml`z9m+o3HlQsAsyNv;J1(e z`)NX832GZMlM{6T4PH__|E|i!BUVD|#_z$06Q8gHy*bB-Vq^vGFIVvki#qln_gS;; z!H1cBx=UpnTg5>9t?wSs;-sa(BI7()HR8+a+Sw-!c5|Q=j>OU-X?z4bxn_m1RR*lA z0kKYu5692aOp-T>#1-Rvyde7**%9iqEb=Y-+v~5SyT0pcf`w|2b4eO!4c`pbaYC76 zW{b8xPG&h3${)V8)Wak*+R%G7jj?npW-&=yRAC*duJmA(5yy_);A}tdQ@C%$xH~4t zwMu|w)`uo<^pR%Y*+20ek^qYY8WKGArn&H3_X_ZK+Z~KT9KEt3f6@C6qM8V*U}t~Q zv4UowSPU~u&+}_px~GaE3ag!(fFIYSJs}=f(YzgO^I!W=g+C9KoK)KE7&ck|KB86E zXo`33+7Ed-981mlB~c#xg<&4y%8eCpdC8!n`g4OxG-_h1P!0G!IEP zjrU=#P%kt3K{$u2ImU$RKPaD!hLI+#?9JXA!j2&wqebekXOt0w`cL`+>pCAd|6CW#5Pm? zOr)cIw?n9dJm#}nHWsjie_t}3%9rqfw6lDke znXsA(Lt3{UPc$TR?W?ebHKXwG!CyTV@V>fe>iO^`B+SQfG2~g);nVWkx<$mRUYWoAI zfwv-p^Xt0yEDeXv;HBje=y`7}X88YO?JR=gV4}7C5ds7c?(Po3f`{O4!QFKrI1Cmb zxVsPT?(P`vhQ&JPs* z8VoGcr@BoQ$1tS6ocq&B+lP_);i*_Kz9;$8?li%VVD!tl^*ZGx((HfVu5=f^GkBDr zBVWN^Aw@X2R(W<>3RVVE1NhN3W#H3h)f7gJY(aP|`<)jH3drxN{W)K1c;L0i?{8^f zSbv?lEb>%i=goKnL6T&mw%pShtNPn(^Bf=8Qng-edkfY!=)lX5gcZYxYA7~nGvu;S zlv@Q^dG^262wdn%Y#4Wc`0>B=MpwdP>=EH|d~+g^;1eAY9>h?`*f6-sF$T~#4|UCJ zIK|$LViDVD)EjN{5IT}MbZ={|ZDqU!+w$NqgC7-tXr=OATzRL9%37jfT8(mMIZUke$DVuMS zzAB*!sOX<|Is7?yTNrHRoPeIHMLViS4`R3`?&HV#HFWs_=+70)pC5?wGimP#lGk07+f9%!^DB^YlWBg?pf~8{WOs<-F~! z=OfJ4ZG*Sik0S)oTypF=RxGm0#d2U|hb*HzXYOLYl?$o#Xu)qqf^Gl!%RGH+GD-95 zEsywK&AFu~K-4Tseb!(yd2%la72Nx`Jx{p<$$K{o1MkDn+lX-X)b>Cu@xiN8<ob+tJMBI|z zI_(kZdF{FMEA_>DQ+um?^?ag$_U?&gxJk`r7d^_|(^e}VjOjJ-3-Gloy98SpyXd(9 zU0V0H#|q(vO+F?D;qHlR&wN_K7(pOG)CeN#PwmhCJM{PLFI&HNzfh1T0tJRB9xJ9J zde3KHkq5`0+E=@O)5-qTGO_ykwPRqZe(tou z=C#|~anO8iHXk@z*!9HWVwD0667*@IR^_TT(;6najkYaVdud9;w?w% zs|tBW$_zVRSMI1iRZJ~nySD*5JH8oKU1+J;716Cdov_@|Apc&=NWTJpm!hVdRyN3Z zDCC$v{D-UF#Vky7ZaHi<_VD}p_~kzNLtl3o-U;7%-l^Z^7cl716*}l_)hy44@;pu$ z7^Lk=dAyS~h2DD0a(Ysl8yVZaw3Re?K*Y~|f3MkU>k1WWj23#xXQ?%|F`S5SVKz&Y z+UF>L^+UE}xO6+5H&nh9a2?K|03YZy-^zsan#m7GN3J$_Owth2Y{6ejgq?kEFXx>W z%vd4oQQ7~C(U1j05kg$a*tBlR73zGb;hnwTc?L<2T4DJ`IQmpA)u)?7%rDGq%%{y` z&0*#(=4)Jc>0d|UW?K*1(?|28d;@4^^B2QX$qj&43Bk7!7^3Wc7>hz)cm1YymSmGc zD?29Rf$T^qn0ur)K6!V6_(%;HMP#y~p9At>a4qkhucNSl?)RwI+zFlMt&R7aw=|e} zbNhSDYbC69-MFoH{_W+J={f11;ao+yLormKV9a{Zi8HchFwc7i_Uqh#y@6A#eoUvu z*X$+i&t3I3tzFQNtiZGmJX&w}(z%B^_rmF-{oKj@?w3Pnzo?SximMHO}X1+Dqtcx zP4Kv?yXz6Vpq$vuhmN!ZE@`AAA3cnA7|yF~=@Aw%@=d~NZk{JddXAb{>T<>g7zHWG zY07~VlR@!f^6za3&P%M1bl@FB7eVp1%gcMIJdO|5@k zg?CG3P9JeV3(6GvLvA*)iqeuOGI=)HpFKHDZW=eo0cG)tt~QHnWLU9-(drK_HI%N* zpAoghhO7X!ruk-^pj0g8x2w`E5h4>H^Q??4?-l%(6z`0FGaXj460i=NR+b3Z$i!X8 z&;!XQp?k>4YJG*}Oj9dvcvcXl{;3MiC#HLPhw2nZhuSB&TdsGW0xCdeDtcfo$C}YS z{5A-&x~w}tGi(Vyw}49>2x3h+H|t#WIrD1?^ZTZ+OS6<-Tu<+o++#vvXSwEkjsXbt zC~Z;IFJfCtwehs|w7<7eX!dD-Ucd!pc!-JAh~$Zsi4?IdYuT_ghc(MI$uy5PX|CFc7S#Mdn#`z4ClsbWU_hbQWfh)TGiJyhe3i2g`u9dq@gZ_~rYR`xX0D z`xW|ClFi53Gc;YU7`4Rt4T)Oi*1b;aWSK_Q53kv++O40goUEO!?l{~5u3JJ|KI#`t zS>LWFI*d*0O|MN0D%alT-NxV6-EtouPSZ>mPIF`%3--F?xV)Y{4c}Fs@-JW3h#8a` zS{qm!;<3*wIa;Bvs5^-3S5zld*RxG64W-r~=D6qFIWz+JR~=iX>Nm}@>^m1dvH+lS z5B&^4&pF2WhxMG6kQS+y;TF1cFf?!1oEve?1^uy?QrsH}ooM9lTAQCFAN#{nb`B!H}J z=jo?u#Az>U3F{#4*<;tI9Jy%=>%NQ~G(nA=nCzGox^Y7w!#=RP(I}7L#1L9D&eNR* zF}%ztI2O4x0y=eLS{R6R_lka_D8rmXI!!wn21bZV_9{|T7}pr*W6mq2_+;Bo(@(ou zhc=csW^m7E?7yr%#@y;3aveHbyIQw2hV*>v)i5sOm?JnBd1p#tVx#F zK;hWsLv$};r$0De)c4|*9q^eFU+kwFyni+SWfK0Q;Y|ZcFTJK0-n#X0mp zggZn&^qjuTVYqAcLUbC>x>f`YXWcqLf?D^gz3d^tLtmH|Bv_Y@9OXJ?|ScAVkH#IDo0z#Tc-?n6i|xI$0wC1rdR7%f>-TV{#Vae zOwqWl8e+R>e?76nk&0$$BQQiyzq)Wq^YpkR=WKF%@oRIB;R;@--SQbyyGHyQ|TJXD#`l%nbO%Y42@R`eJyJ(YnA(47j^)1@Im!a@L}<>_aS+v zgSEgs>?+p&Kp-IVOOs0*aP#5n?Rm~w&N%|?yAQ!3<}}5*;rZr9=K8_Xrbp}Gd6Q@B zeI3E7&zjGw&-(Mq^V;+3^E%QB(i+k#(mF>oL8sxrE$qU8J$IzHr1#lZzK4&H&9Ti@ z!@um%Z0O71$JyJY!{kG2FbBw3q}SV@pO_d~x?r38MLIFT;o}({j0!e!CUKs&UR#IR zn6?(SF0&%DCbKHDKDtud6xST*`es4llxvb}lWWmCF1ji@?BD!K{QTpY=h^KUt>@R) z7{QH{FPHJa<&NjA&x86QlRrmq9LMhcEz(2q3sp~cZJqJy%j#p)t@h#Iv`luKAjPe( zWf=$ylmJo#&4Re5w;r4icc(R{8L}&LR0Nm5E(_+W>w6hpcb#}o-F6(J`wysQ_ugI# zJKbCkE1AWnvpC|kMj@Exw<&DUq(+3*ebJ!<;o1++o8{>dY5$kYnnrA!+lJ#IFI zrn!dZ)HOOiq3XiaQujHO-!*u4hD|kk{MDUH5-f8-yY4249z}KSlFDf<`V697a8v(U zv0lAa0XkQ@M}}*+t3;FkZ;7uVECyn0s?|S8ienZ>1ae)u(oPyWH);sA$+Z2ngILD! z+<0Az+vf~3YVd3LYG5@6+Sc0I+6k3$_P8x5b=TZOaj-m?1~e)h=cDBVy7?_k(j7<4cZmaOhPXq}A%|UHA7fb0f#1Y)i)ZIH zk3r~YIPVFu^9ZMTk#n(Um}jYH+P_mGa`_Aqfnat~0PlhK-eNcYXRkw@TRwfy<@b5_ z@%MH2+?R(C8b~38qstiB`-S+cnESHpXuZm+xJNjdRc<}_yd;_b%nHJ z@Z#s!Y>c$Q^Q*627=u{5;qD7A3$T}Mf%A(Ufx+0i!f}rA+Ht4x%W;ZvTd=N5$S5>1 z&AYuYwy>rUL^G|nHQR@EX>%m|lUC3dqnBnZ;q22_by9Jeio%4#dYY-(q4xRqiT3qJ z(5>Gg`61FYW{ywJb2f#5*n#n_(?jb^`s|9VYmfM8d3KtuQ(bCP1gLmrZ|-!O z|JVu`;>evzcr1-r&6`O)6|Fc+!gBGZqX~1pDCCc3ZTYo%>^ouOXhFgjoeHJywrDb| z7B+6+h*^>**~Kobp8aQovqmByjZ|2eV82K7D9upVsAhsoY#3c5&0p9#JIMG2NZcI_ zk)|lDodq8`q5bT(MB_mu3XV~TF)JJ*G&8V5<7FoZ9x`z*XT*me`et6mHBIQG$VpF} z6kQoTHiElrw@dsFeUEA8DIwvP!QS|(a1dFQht<|pJQZj4?XQ5u;VAzcnIaO#9O z13bI!H_Rq^9eEwO4~SYxEJu80LI&NiUnhH9zA#t=vIokq0OK*xAe@Ty`2(J8t{SqM zoEpQLO;D}KmdP;JWEP-^P&}AKo6wUGHCiYpWQ4k9M#q7b>*C7=-vw7H@jdZ9(M0rf z3{o^wOxOywd3${jm|u$+lO&EXjyR5}GI}(IA$oBnX@t0AP{&@_Qdo&4+B(`@`j3uN zG6_j^%$dD;(sk8!cKzy#nFB9~--y$Q$LNa@Ukgz!VJ&ejQ7wrRp%bwa(K{D}55m<# za!D){oioC2-g*tX?&<9546Gl0=zEx1shm`|B6*IEi;jy?iGCe{gP}jCl9`WgUULac z4>5AtMdQzllpwJc;y{_J3+PzW@Ed&jg4wJTRt(Sd)+Vh4&o4qRK6)0cAHq75HpVV~ z2@R9Em3EM{lXP}^hiG3{TwLe?@Y?4$ls1kp&}t8wz1?d!Pp<^K*=vPH@C!#*%t^V* z<_JBb8AeFVhyIO`g~)O;4Tcg&#qRXB^d~NKvmJBd5!yD0E5AFxt93`?hTrDv#_J~h z2K?sK=IVv`1?a-)0&*dGaSZ(ea0UM~wl5-%X|i2&JJ)+FagB1Vy!AdrohHcP5HR*| zbnO|~+Iio0y7heUcw^t{_q;Lmau({diO6yb>0CMqZLyF-QfDS^(K`$o^ek1EEA%d zEv=YeIF3J0T4Xv|EJ(0nos-1*^AJ$Uy+}g2-F2cSzAW_)rQwF--;nKqPCyr}y;UN!@Txk^DzWcYM@l z#A?ve)cm>7S|6>~J`KhStNC+`(uwnHVy^mTyXA6yjvD{qhfw4Q(ExQ9O1kv5GH1Oc zXmrWcm&Q!%R-4rd_bi3A7W>@!d~-)5|I%rM5W^0SQ%GT%buGm233e)G@;9$P4L=R7 z-Qj3o8;ylbnMOL6Sma>5es@GA{p)eXm_|*H)k)Jacg^dv`|`Wb0XqM5rr-ROt7C@G zDd5(TBP(DuNQi)4uWPTS^HLXh={IpvibSE;W41pSC;YThF`BFMx|i9!Q(l^@OQ2`m zW4c>nox9#;d3wvy^)NTFZj`@l6tFwh`gRKwTHo5NcU^V_UcQP@__ldc_`Qt1!RL|- z|7cv-{r~ZJpPRWk{lAZQr!$WOLqX5A1^XJ%m&Xb@9ENBLe%Lryp^Sm3jY%%VyPfQx zzq!*gERnBTzu6&SQ<$YucRm_QZyQ5WjHvNpOwQlf?EByY;@$4aVznU)-^dkkgS~XH zW}Qb9D_c$4G*6*iELo3OT^)Y2gEs}%_wy@S?T#w(&0zV&qv`iIb(Zw^mw4|_0wsEX zx_eC1>%@q{*D>tQ2t=W|vR+G_)8 zgW~pF3SZupaN+7C#cI0XNela~>ihR%vX8{)DHPdA84 zTvMxL`C7ZKDh4)w_suOKI*m#H*<5q)MT3~uSz+o{oNL(-q@bbpJ`S`@Ed1u_78V~+ zM3Ie)q*|h|XAY(N!FPlGaj^6^m6i?enz_LrNPE+y4U(tC2xhrB&0mh^jG4DUoKdhR z5xMhh&$e3|m*`H(tu=09xOnJYb%reS0SlnYOj&jqOE3I-Q}&&pIRq5lZZYw6Ro z0%cj1>X%z$cYC~80D+nN`-qR*O0Uhgyt1u$vj9_F+akv(M36P=v$6Uy0atw;60>m8 zM>UO2_#@zJ7_I`+OL;$g;DB@SCg(z*d70Ls85US!fS#PAs%R!4Osj;IZJ0eH6?>uT zP0X%QNd~FKAiY=N592w$f9lu7#5dETsRKEp3F^ZFsc~GpH2F=IO*Y|dte?8$K1}#|zTCAJ$X(lYMPZO*HCcL?` zx_kVLUhr9p-xA^*vD7=JYTV!tzv!g=jyG=m8$6w2G84apPKJD}uk!ndR!rR}D58R< zADhDkSEs>^mcEWF&*f7ZB4-7>$kHw37~c|~?G%fVG~_dOI+b2vCGC3|HW4$WPng4Y zFWOMW$3z#feAlE6MRR>PI{r&aNR9B8V9VT^)CU~9I$f_{d1Cs0+)O;8qCEAq-CzD( z&75JqeR13IXkchbf5K@8E=&!ml%(rNBfD{Xp}$5TCkc zi2b4@hlxhPwiwT)j!HxZqUx{F6#CNqFz`(sIp``+Gp+0^v<&Npt3i=jAT6z=klt+E zY~lqlG1JH~n)gFp27`V0KN!gyyMJYQ*yv!W{1H2VPL`2$hCCS#YwJk?T}j?JpqBWBj;CK%_1GmN@+I{fn(UW$uG= zb{^>r&c0rdQ{&ejah~h_gJmq4@*lnuEJFltpH%<+ih|Q%=R~l=QJn(pG@^%uta@jO z&u8g2bvf(7@Q>9Xd8V+a9qu<=UTO+bjFjb=R$q)%&l6m(rXE^{m6;-2SiW!ASJ^Fi z15(_uGUu;Lp(9fbDhHk0eM2!i&)he&^4Clu|0#R#B%w2vud*27Uk<#JUGv2>6m%P2 z#EwY^IYk^Y;t%_1IYd4{*Rf;zh4kVk+gABNQiOn6LCQRE#$iUzH1eb)Uzo|Hc@~}IWD#r>!Q2g}831p0 zfw-`NUesrA#WOkiGrh!&eqd5EJ>wHMPIDq%33YGz%`_qi@h{TH6F=q?!;Y}44aBpS z4X1urtUC3zq2{tto}ccWgvOASFlCSZ&=&92c5oC|*@-ZhmBj1svyejvkFc}wJkY1R z@w!5`tyz*Y_G3YyMDTA;sWRk8a<0TT^q&e(vAXqVbfNWJ<0523>Tr+F5Q7Be9lZlCc=iKSifwF@irW49Rh! zUHCgAaH8FPK~prO3g|&od5j`JtRp!X%wu*i`h82CO;BUh2HckSlF#k=#>7dtQ>pnR zx%=mep%|0$y|EOjhCKO(6U0!Ad<2(8!pHC6_30Sv5by$(HSAO_tP%}6GBb|Rok>ST$g+G3{jA%2}&J? z>dLfs|$uKansa# zBtGBM-mA!6P$X_((zVwryLiebUK5+?w0Q$gkCRne7J#riP`S5q?z2+vvwCi43^?b~ zYJ$k6RC$Se7C17&@a$aJ+NS_JT#ftjD_O@|ITMq+Ln>Vtl!U3S2Aap)bMT&ai5DAp zy}0S_^S54d==Wz*5jdHs9sE~1j%<3z%@?b?Xu^lm9;M{PX<(oRiE4pdlhEE7ceXH} zX_BiZt}r!>Ss*A&n=S#2*q8k`(O7@(0&k*E<4<3cP4|IL-Xm&l5KWM?(4WMc=m<*G ziQvTk>wN(w-$$-9dHL-xXR;_RV{%~G*vU$g=zO}I}D$Zas@WL+LV zpsE_P2pN0#FxUu)(&@NC9+kdAvu0$UK{zVwpA+to(P%;PwMw< zc+GpmbcFe|K)kJXEaxIz;D&!p26dY5=jD%KWWNm+vs)kjY69 z(lH)W#P(t*+FoaZnoBJwca85M(%Vtj>t?-7ko*I&&_WcUpHT*z$N`>i!X$Z;Pp2QK z*FR&Ngo?Vvzz_!CJ>D_#?MS9pFR%u!G$n{NUdveSb2Ni`WQrDwN^<`U*Fc1KCH(>Q zK{NJk#~=BB65l3KLdp2(JhX-$rO$9nNb5Qfd$2cBkg10}qs=~!1)Y{?bPnqLS5m|r zD(ukHPf2gRk3^fty`Lue(8M_HiZ-TM&~alC;tw?c52vGan1{DDwX- zg&!=yJsnhbk6|LK=9&{_x)^z=MlpSJDM|Zn5U5PWPuZP$z(18{bhDiCcRzuo1*^7= zWSwEy$+D8h2r=9ILk?rk4$3Irk2j?pLHD&EkLjW!WMJF*UDi=s!Y(=Z$nS=GZ|cg7 zrJTvyda45!K9nZTMyIBMrv0WdVb$vNNc2by{GcxRW1J(Lv-vhKGaY|C$s{1VQ+^E1 z0+E}&Uj{drByz|(NA8G;E;&p@N9fL$<=C~by7E>kzEE|XtXE0izNe70hBGw z;%#~px#vEX2_@8faWI;W+`CK~c7satq2~^Kh}t~Pdq`TcowpP(3F=H%f5v@w=;S=} z_em82LzZil@f(M}Q~6SB>_v*2rK{#kr>w6v|2KA8&}?7-r+tpd#nJV`qk;;M)uJy{ zm9y}pH??@*)~{I|5Xq;nUsSzt7S$xhXLORilYNdGI)6Foav<<5sF@psHO_|@@6O`~ z^aYvDqxdSz1gczp$Tj4+8arW;%LPH&zlvo@e;2cw+0RmEqx`SwKg9o-yF(Aj|9-l{ z$(tb+zWhLrz$Z&E`-6uqnnauOG>_I$e(q~RH&vx3j4n$&EDm`dPGX>`&NKaB&w4a4 z^|0mVo=<)S_gdtkP#;5IYahig<_4*jln3Y)A!~-b#ynA^RGnf{T>0_5jv$&MEXP)3 zeqQc7hBlPpqmuPuoSB(M>HI?ZZwNxrx75=gM1YQ>Sxn(SD76r|A{Z^j%#7Y*!Xpvo zJICZQp2#Hk$w!nQ(fpWEdg-yC*k6pY3bqKvGjdDWM#X^iJn}G%<+O{Enx?3wgs>X( zb+uf$GUZCDci;5s4potoxi3%Yp>w=Y1hNtN&Txzdogxicjk*B`d;ef-tSRnR%H&|d zdG4kZB@1{)oC8N;cz3L#kORI7nV2cE1DQGzN#iRW^L1t@k!(b5Z`QA)u$38x16U0Kb!^7FqE-!v?U{Kvi69M5xFp7 zZi)w!G;N*_qUffjotUd$4u4m&ef7%kH)&UYKn-@V@c5F}e~0{(P2oHe3GrboICc}6LOhJ4xzK0|;kBOf7GdlmrBf^vztWej zjDJU5n~Kzl=&EMYD%Ztpw*oZAKBm~J<1izJu^i#;IY(Mjp}Bk>Gs{dYQ`Be#Lw;k& z79iR3cO!{n6hHz>wsdfO#gaS+CKaX@4+v-j%KpftsfrPQPpT%Vj?2?DE?z!H^2l7r za!I@S2LS`;41zk8ik1I%V}8f{wst!!zu!c()y-&$c3X&>RtKpP>r7;`P*}BsaXqKo z3nOd1w^Gx#M8V73Yx1sB6?eGY#TmIU#YmxUgs&L|-PDxu9|5$&iW^E8XN`v5Ls}0J z9z@4IuY=8Mbjq^ToT|SXI9Kg7=}PlK8*0kJj++phuS6&zQ_)^H=4x=8Vx3NM$ zg=pXjY$MOHFHxT7ZZS(_E$jbj*t_0&nAxm-s| zIDK2Vr%RWL9TDS4%Fr~rChW);D_{ns1oK!!FczOO^&@DM>R#^1-E}qRf!$oCUVDY) z3;-i~mh$)h(i7ut51J6VWq*+uCD`hGbnHnBU?Yx9h%acoHe?6kQJ62O;c85cv}Oq! z`+Z+`w0AhQBFXRdIT%`{w^o=YugZ%=#7om;t@bM3VR5bVCVRkC*RVrWc7jivm_gFs z?nD&1)&(X(QttWtPvfGwH6bsSaL`b+CO!63l*MwP)8VIWp-)>1tDk+t8nGN$dm2|9 zJh-z49ZgYJM3%H6=0?LH3$R{mZ0pW5mC#U+#a07_fFKva(UdqO-1?@4(boFk+S{+! z-H9qAo+!Pc;BTlR{rp#KzO`j^LGi7F!;5AaUeaC6qMv(N48~Nyq>x(HxfFkqSuO4= z^)1TQ^0Uhq)H1B;sTj6rYZhPgIu`}xz?wZqN>-n~qvqN^_gS5Y%T~6kUML+Jih~zC z>dWl_`^GPyN2IdT3n26Xa8W#>s*1Rtk@t&SXGK?yDmod24D{)7k?b%0Tp8#G9E}#+ z8tV~UZb%spZ12&zJH|}zE8pJG?W;Mj5yZ0KpKY~J4m{SFZ_9ApbCKvj!3@s zUBBmlJ;8M&%>Cm&jL_nVd?NHgVh`M=I+pqyV(acruarBZT{QbUPc~>iNq)i@KcQ1- zs2fR-rOp&2!u?&{M*9AHfL;XePT|ZFC@8fNjwNcUDa2?%?+DL+^8rzd*hxItXB0Si zW4IP^SL$2nl6!}-3TGo1mS;#7>JPIlDpGhSP=%}Z@F08{iIOOK<6UqXtP_#7xb(xA z7J5fpHln_iEJ%kpUEm8p>5{`GppFz%PX4(lK>K={OVyT8<}6V_UpZ*g5>qD7bhEUK z)BGCFXl+jTI4qgdug#zL=`;p1AO56-Y~FV&In04&xRh{&f;4-DC0VEv!UdZkUdTh_ zEfYt#Qw{zqvx5KMc2Ud*9@)J*NO}(zpq-WL7PJDcK7~H_6NKH$0_(8V{T8 zEx{82-pOgLmF3lTzZ(0OyD#^ZwQ?$A;_VFa<^s0LkrRMG)}U4Q(}krEW98L)Emp}H zX#O%9O*+lSE<^PANMQjzGIWYx&fi6Q_|AD22Q?PzXzmouA-^ z6W^rETq>vMEFE}A5v=*xtEF6U!t2huSnLuNyyop(?98%@G0KFSMlfnUQ#~Z|*u3&J zHEzvFZmi7HcF7bY?^AC=T1;WbPb!!>D9pQiV>-ib+0bA{3&s-U368q+>hk8QZs=<4 zit1wNj%@Yst`-ghK$O<7FN}2lc@AH_tT|+WPb&X6W)A-nw=fxsz>6TY()xpQ&5_b`m!nN zn{jkEvM5XY-lZc3K>68*c>V)b zAGQ-yaUh!@q?XSTHdFM_hHh@M$C#%A`rQEX$lgvg5AQhl+)&;(ePEPmOdbhR& z3xkDSZLC0B*RNz?nt&a%dCZ=d?djQz&ndF1{jl}&_XpoB{DH>D3z=78fb{=R9tb{t+DFgMNc_3A3kieQ zy$jd0`sE-FjfQj-p7kaFXRQ|G%BLyG$(Bc_o$blRe*B8)jeDRZ#KC7rQ23nuIqi&p z>(d8H#cHm$-!IYWXoFPDxE}*m|NA(|iJNDzl^=l6Wn(%^J<~haO^WwvmYU$|qqrAg ztT@Vd<)W{uRTIrSef7Jeky2r2)p8mdy&?!vBQk3}w?}HoY~gwCDUDGDO4#3h+c*Z= zq)PWZA2fOMK4yFZ5elgd4+qYLqA)cgs4H7UTd~>lJWEJ7rtzq>$>obt3$XCb2C*CA zq(wv(73XUv_&awuEj3fQ1$$My8QN6xSgqOis%$WwCwqompo`Iq(w{pE;{%m_m4UPG z21lbiaYYn}0k}^g524VWhgj%8Uy_~NA~B?Q((&sZmlTvRN`E;@LKFnA&b%AYFYUXZ zxRoxBVG8bB_}FwxCn+uKPEZLC^CCod)s+&EN6C6l|2{zqMzVrAC0aIJC2->B8qha0kz*0Ur zJ2?xow5ijywr);rP=)fByn9KxO>1)7FF%dDZf9{lNCQmnpq*(;bGg3HxW12fn&m$e zlxQW3q4CF&n7v+?#RrlDKf?F;#7XdS-!NHEQ*)k+btQR7!$SYT1DJTG3CwfDR|G>M z`L&V8DVR|*7N`V4$ATb*BmXsnJz5z9De76@ zaCItQa-O68e+=!^@qg$f3;JQCtFPF}#LzB@PJj;}xmujyt{`f(mlWPK3<~AOAoN%u zOCGFcFipH%#AReQ4NU;f(xE zOpVP(DF+w%Pp0qIw_&m+_7ykJum$ni7)OGgd_n+f?S}m}O12+uN;RtLn0ChG>E_{0 zq@k!lWlB9dL1wh@y8{dbZ(nV+_IBMKuD2@tUDr?mP>s1=pF%RML7kVJRlN^07ks+z zk8B4d*5kG05Z>E0{g@1~=s4khLFj@pNbK3X?I47Xyj1VII?|ZuE#ECl5efBAxZ_6u zC_7{bjR@;&mIZ_v;C`OmbF{JRFXcz~kU7MJv5JQ_TSamK-6AQNBgN4YryUQ~)Tcd& zc^I(m+?0NtBgg#vp}W%JlZD*UUSidRRlDr%vwH7qih0Pm|F}6&0s0rdho92=cZ3eeC-w-$P?- z|JR)5e?N>;GpK$)#Xn!KU=NvZNeXKP8q>WtOLMX@tM(*|Dal3d6*eh%yVl+EIqDF> zHkbHnqMuAzFf{Z#~o7(@2W%HiEOZ4RiN8^g_pU28>nA0ny{8izE@6 zpO(oe4UjM?uj&kPx87`~D;d@3a|+Lo88I8Tgvu3!Vs%30+1?yr&QcQ%*lDt*U&>o>r*x98e-k=S2I}d!)ag=TI&d{5 z@;sCFm8J_f(VltVpdzfYrp}iwS#dhzZzsTgjeO8v41vK&rKDWRYbowprZK&{XAMH8 zxxznUVC)~2b@%u1bK&Mp{aKBr7vlFOQVR-|s_mbQMKQau8;W5JbQU5U`Rgz=o3oD* zmquy^BuJRf1Z-twKm=|=#A4Y3OpLrBmdR6|ax?Dp5r*8YZx-MVdwTM>>jr>+M~-re zv_X&6R&guo^XQhJlhw_(#RrIwl1QSW7ItoWyNe@umEirkZ~5)`=}vxxjD_ZD>^~F1 zpsz;oy|mCm`ENnQGkN5ODuxQ1@|&gSvpxADrMjj~a1;v@u&EsTa+dG76k5l!3-iT- z?2#y@nvp|Ib%^rcWM*LeaAA@NX1G&xy(*J)ewzoN2f8 zohGZ`wlc-_$wY~1`PX#8Z)rR5AuyqrG?htuGUpTzqntApCtE~V;ku5U7B^A`w;cQ; zcTy^Uswf7IR>Yq6otsoPOnJHGu}xOAD1 zKZ9;Smy)AMe>ouU>~yJp#Sp3Cn7Xab8n57czO9Mzdxct-+BE;_mktYpL1u~wJJUsq z2>by%LG0oIW`YPg(?y&JsR28bG54W||2lr{z|WL267o;6;Th5m>oYUU_7?iwKlzDr z47-y4SFFxFmBKEA7*negZl`BqY!P7b#YNbKdO(tVS;z#b=!(uEu`B|^uKL02Ipy6H zD#~{rPx)^0pEeK+eu@&Ck}|T75qdZrI;dQOT=eyK;m_j7b!EaV%;-E!LZb8lgo3kz zy@sXy`b4|Poie6G1Ndz9TfS*BvAt?*+(^IQ2u^FWwejs*(zqRgtK_oVgM!W_NKHq0oY3Zo7h7o+NfEzrPI}#xYli zOkUWrc*``e@kcDmDyd7O!t?Kz9s6fX=i4ok1qCs4`uruqe6TVePA z-nYw#5Q#NY1lj&~`XB8tqD14JR7FtD#ZBDgU|dBtZ|l}uzC(iY)-qSz(`aeSh4t^W zkwEU-&@Xtm^+NlQ2eED*Q)J`##O6Y;jAsL_=42oOvmPjl6NotT5+`}ws^ zH2pn`IxIiW2oYfDU|~g2(tYU^rbNme#=ZaNx~J@4R;tIkQLs@3A$|A(Y`gmTYqNwj zLM-S#E!>TQlo-SFoKer1_wp@61X22aR2(9S1ct|Eb@Kw z`St;4UtojRR8W6aqpUz6kF%v>cYlngz@-ZSaNNra6Xhxa%TvFkmXpw;U zY6%`ITBI@Ug$+ik6A|FfBzL%_DaW;_l1=t0)QpByJT=qdh$~uVm&oaxTu~B{Qv=#A zT$B9c+Bj!EHJKk=9J^toQrh=$E^}AK*?2xA$tKQUAFF?{)^kZqW2qALiAGolbA6P; zKt~A9?yT;A^Edl*PQABNGD)iPC&WHM0lyHpisD_%%CzJ7if5C633G&I5#_qQuNEPZ zg=LLI!bm%YD^Xe2$WGL7P0j9@ zKQqW2A-k;AZf*jV%)J?nm8>$(;!E@G&i$DGG-Nd>Z~M`mx*}WB$=dF}?McW}vYS<; zTlzFZvm~W$J&IPO8uqmgwKSKhCl6+Dml$RjwH_Ur4fhVp{%lgY){m=f@Wt3*dbAdy z#ZJInGvKIpH30skk-nqRzU>d6x!NVZOnxtLKk})`0PgiH6}3G&-ANAAvc?2m!mzorD-F*pwi^J)}Kn*2UNMT z^!UN(KT(_M{E@nXJD7ZvT6lr_JECnG&t`A>SNah&V@`+Q;mv3d0$)mh?UUIDoGbf< zq-*ONj{PmhSM~sQr=&onCnkZIw)XDQ8I2Ble?K*w-Lh~0bqrPzMg&RfAsFa1%b0=c zh{K26nA5^X3vu1laHGYPY;o69y@Q5}{tmZJ$U9I7{h8Z66HPS>d~(a1`xQ_dMRsh$ z`7Odj==ZOr2on3??Ge4U3KVLvWSQot8Px;{b%uomGWV{ei>jZJK|l9I{@<(?mnHj| z-_{KWdB?vCxbHfH^fvC6f+!6}i?$eZYQaN4R&$X5XYrhonHc{-NXTYYsum39P&yxC zr!CsfGq|O(mGqTf%}Mlro`od_?JM+%;G#0z)SD5tgw6bOK1va1HJTF~|J-!+`JZ4n zG&jhmZKxdwXNg!+&vQUi!oYQ>E~LRi0|iy)M_ z^GS~8)}7lpy*!myUlxT{`$vQhH6{5QEXKUE_ld0x4br-QCN=%vd-vt#s{?p(;T-oj zx4sy2_Q}@&0AE0$zqn<4BRO-&JsYgl=iyVI*|FD&kp0-XUz*HFK6O)j2%Y=nhjUPb zB6hQjrZmWyv#a1Lig)xh;3EBaXLqNcR!y6fO}_O0S@ zXgEAv94_tR?y>B&?X=$|?#w=Ada5kRwz92as|L%#O0bF@WqFep(!!ZczC0JsrRt`V zuM{eUOOcBz7gd+omb9(0th23`)@9aZgJBq^)N;70cCJy@_}2LQJFX~SQMsaeMd!vY zJ8iLKoE9|`vuHhYGNqnTdDOWp{zdkS#XY5`ij(Q@w!NI4>^a@zx`gXi!4^t=2EGa@ zxC1u*Y`U%bXnQ~nZV9WQ(DOmuQt>_NLPD$8YIj+!_N=wdZc6f`U}A6@*+S`dI+n)% zH>iiA6$rygm~6n9E)^XwIjFZBP;)%vc*{XM8r04s;ip4c3CRY0KzMHfzEt>K;T%%N z`r@iycol8Xpr92>NJX0p--e4o4K9L8Gn6op9YI2R^vu*WQoGZmh=>f=Jkhk+KtL<( zYqev`0G#&{klFy0My8q~y)#~7o#~_{$Cp7y#A?`yR%{7v3VCZ8Sgg5hN-9I*lwi!yG+Gu{wI z)HMl>Fl@|G4p9c5q>}L@jzJh+!_F@HTM2pK=_{_gH#_&;@7%xqci)~<3ICJd7i1F& z|As?D+yB0+CpGi6hgO{U$DwWAUVlU|-#C-qx$k2;E}dImzWtg{eE!l$-{Q^nP!ay~ z=l*{1u8ken+^8;%>_peo7xQMOdxd;#L$9NEi2=DZ4gd3@i!HuCC;f>Ud!5c$d zF;b7LihR-hi29WINtLBwFyz7BgeZ?Cd5l$JAWTUj7imzFx|4@lpnL6gSws-C4?Kf3 zeS=EtevT(HmnY(tCo-3hdOhK62+L|aJ_CfL(5BG7kSX*$l?I-3r*sRJ$Q~k#J?QEO zw9Nz9YoE=Y8Nk9fgoM4NiZ3u>5i9toIZsNxhBJcnEzm7hw6u1;P2k;hFM?~*E7(U& ztjEJtC?s)VrNN##G!&+6l7mP-={!6ucRJ^?5Gc&rZ0<0i!*%fnwJnP zFHV0KWAv|XN>`S#Ny)ro=6h@6JzXE3Y2k5`)$Sa+0?tJ}pq~3ByB+mFjlS$c6zMPz zyE91_z87qeP$??FmsFEQrYsyN7VC&*>UC-QIQmS&B8~|8c*e$BIe}*dp~Tc!*{(Qi zRQQOj~_D-heuap)u z3)tnBCF&Yx1G|Ci7dBWo_&1c+v|rC`W```-`L9!NEN?Y!Wwx?gh1)E5Snu%Prfv`1 zrrlD!+w=f;U*O*2y`^34pJVq}9&$dU?D2n5{e1f2;^#|Gao^;>Y5AuADfJtHZw9|! zJis2{jtC9^!O{;(f9C#dIUD@5w(Pp%$4l3>@8V6}>QHD)_>+0l$Jvi_*YWgnenogm zdU?^*udXj%TB7^dK5nChHnBiJqBP(swgocbcD82WpJhl;E(qi~J!**$m@E$C&8jlT zTA+oirDO_wE$TSt^L_kj3Z+_2)pL9x!125gK(aLy;s66x;PksxXS$eC)3Oy^B^64l zsakutT5FseKA>6zt#NLP?kaJtX0=$NDmq&A2Ld5p5QyQd22eww805Gp_Q{lr?F`G} zJ%Lg?+H7~qsdO5d9)Pk40>`oZoX43b+EJfP=+$;i>OG{;llfAm)IQce-cGM--_$2U3`ErLXF}qe48WL4b8HfxOs3#Hr+hs zmX`;*k$^>DB!=Vc$yCdei^VW>rZ6tYf3q5)A%9wJ=ji1fNvBQb7}S(0@0bo#?|-V~MMiQ0+gVG`P0_pPXFvzIh`xVTL|Lor zSOnH}EJ58WTS#yg3&!!dH36(;?02!qGPSns!H1yj|2&9AC#sy$-B{w5$+6`cOG-9R zjBAZYrxeN{WXj}96|2spi(2(y5U(9*m$h;3H9dq6vRX|$6sV9^g&d(si4fbHGl3$~ zUMJS&209l^xCoZ62hSR7E9p1G9|C>gt8W{jn$vx z4-2%MmA8ZK<$J*WmiwzrQ10f%qbqv!`mP`+*jWukm%`=3Qp?in z@~-(kOD|e)xz=(wf0uBVMO@?gtcMEMH`S>@t_&)@g-pKk0%BS#u%4Sd%-5`GOU+8` z%bu!)ID*1#F=(YVQf#%Fti1|$0%a_|-H2fA0KG8n9|) zbTqpXM^mI}9B#=X&DUgE>4Sk@P(L$$azL6IsZWg}+?93I@G;qJ(GZ9ev=xGZe(dVN z-9_-#;_940%7!cLg(P z#r=D4zhq887QCWlb@vWm)7}Fg$*o$ju6xB@pK$m-bHn`h1-Gq>@46-$&Gi&ID*1Ke znefHgyJuec>>L+s>+Sx+f`{NhuP--PTe1nsj&mQLJ4ruheh_#-9DZ;?c03z0V;LY} zX<&9)fkND~3Sxn`4`NYa#e|BRNCis@D?Z+akGEPCFECNO6N3>u+Wa5KlP$&FrM~GGcq2`ipd>?E!oYkuD`{B;=cY?G4kYs_Kh5c^)(WKPbv4RmVBTj;H( zd+42}C+Tl->|z%7aITbXo-^cHpm?poqg}J!~E~Q-kI)^D}1J{Jzx#fD*S_ zC0n0u*fwr6jiJ?k8x3sO1yw@TN!xK7YeVwmNN?3PnE2lEW=6uuC~J0(msv0ut|*$Y0xhy0pL?C)&PgL!lsA@eHJwUf{dyGBf{AXaNc<*Vi^+O z9PRH{54ZspX>i_yLln!a4R&f`h;Pv=)^#`_>2|RO{TV*MRHE!~crIAs44iZKqks1= zU%CHVeNU_tH6_qyht7QY6Sd2~^rg+!YMT1sx!?cunTN-Edgw#{u*5ILhNsih|ItzY z;Y<5pR9y(IE<$v+4C$x{6~$CG!I@x7OHuxhg#L-9VLSneT#&;W}k)U{h>R84PSyMg#ZB4?4#k2kZ}g?{X&t?`dbC!cfcP_457U`?ayy=@{$K;J44602*4tNGyRMz+%3H zXlF1o7NI~yifEC(2!5{dS@E*d5nJS%;9DXTf8a^*Y!HVITs2(i-Es{*q$To`uobSd z?zU3aqC{e`gJ2kpgZ*FF(pQ4%*_1g z{O72@H_U6C1v4-@GCeYIa)gjxHd~*X8X*eeq}*gfVRi88;7vjLp&*j_BmIbfy1Tn! zH+E=}z&XKQ2Z5wuYvJ@^XU!~0*s(N;Br=k}lg~bf+^S3Zn2P;jxi9)b3o<50HI+d4vWVoF9^e+dkpMD zT}`*5@Z=_pZ{&!pGmd4S#A@t*mImw~QvLf_6Z<*SS48 z6IapGiVS&LRMF(fHsc$KVoUc(o6Mrt9q5eoa^jtL)1{qFVyrD0V zYxo^sp+ejv>`ypPTCIs5xW%)>v)e;^&bVA-94v9;9980Wb8m69#OWM*5^g_tf@8RE z+TA9w3)2;y)8&#KXB*PMF4;9ZecBG~_)d8AF75L}^P8Fjfw+~^BUo*W44`5PAvKb? zI*LguJBru{qoplwgqE^6h5F0@V(S`lU37PYkpVb=!$Af>iW=!B@QyfpM~QcsMc-zL z)vS8HW-zUG!-Yzf8|PquVBUvo>onoo1oY#IrD!t6(IRzFft zhgjC)aDxW>>qA3sx5c57+&KqpF*UgG#E|8JbZ7x9C*y7A%NcS)#&RMNNg9r^pTo_Y zcW%5pA9nxfi%-1&`@@gEJiQY>WtMzbcdq^{HRmTc-+c9Lu3f)`@MrHs_Q&7s*%0s6 z@5DZ>RRGYpn;!sKYThiVkjU#;JLPq(o$|z@QnOI9Gmx`qAV=beGU}uEbQv?YT_z&K z2q-eo)4u{df4EcC`I-f{X0-e9mBM{55EWzW&%T zBhgva=iaaFaqmge3+M&bCBD1qyRGI&O|Y2X5gBL3*}dFe{&DGX$NoGoF-X2`YTJ~h z0-XI|i2Ga=9t^P!n$yFv(B9BXAu8mEC%iD*hs>T*TSj&;94knOF&gl)1G|x_)1W?> zfNj|ZlyqA<17*=6eNGf%925D0!NCeCdwPtrUN_2kJ1KQfAW|8(L(CeR?8Ej+`*AyC z_vN0a8JcZ6jSXU6IfY1`*fhPUczfXFDDmO-_D+xX)~At8Q*6d(pQ}l}IC&!|> zcs#w}Dr)1B-d*3hWkJ3>RE>PX>1@CD^^2Dvm2lC_BKj8yU(EqaK|eiBe6pXBJ$;WP z_jJ-AFI`S;ZQHt<0&Przx$J(;RPS1K`IfF*lEatpHtjZl)_a$-yL#W;&o11({GL@0 zdmmQztZJB^GavLGR9>mPvV8LLhs;ztfjtT`(t8QqjV@~)m%2r^)b}QyNxYPx6Ak$Ip$*wF1g_Dc zx(y#I?~ObYc_~6in&*%bof1LE>dN>sxJ*Z_%XHMVEQeWcS)U8K8j#bSTR6CbLlpJj3iQ|-xCTF)4RJZD{OF+MB9;Fn*83VGp#?GE znnXAaNnqOJ^;dSM;i~j-dNO@HZA#nmk?B_U466KAm$9Hp-=bY!x?I0}AA0BJ%kilJ zi?wpOefJj@!9~RGU(~L7py(O)yo!`dPd-OD@7j@^JI;^*s=sue2 zqamOrnxgTm^aU%VL|3Kp*JEsrD@X7%(${ai{CRjAh(O`lUD(r?C7C#*Q=`+QGL;=Y zDP>1UI?hOT6lePG<$2Cm70eDIA(g0YUxl{uSz4HUaw92+3LCw|q^qp+BnNWHsaaQ0+ zKO7;dR2K>oqS2}(WADW2XXR!*=vmIyU_a0kLx@xn!EWNe48K$oGPAePc6H6hWBWck_`R&1X3Vshy|w%36AKpS!jV#7 z_@@^QY`NhdKK$<8%Po#7yQz|`LHDxF3o3mpu3A{0`BSmfv-!n?-zrxg{Uy94^HBf2 zM|Cs9d;Nl$Su#9!*p;lg92#q)&Ae^+vXQGlx4xrOQ4$yPSBKlfv5!$Zx8DBv`in^s1mr(i!3F+deNW(6gQb$J*!BqNn-Di0$js}-4_z1ph0N* zPe@bKqoQG5F-#yuVyqnH4u&;Ztdl)zA-B9C{OvuroMcon2 zXF{oEQ6UqOmc=5OP_jYW^+K$gD$J{dstcf&>ICE|5%P05B*CY|`Edd67oaE%3wwoE z1(Se9VImJA@j^J?mmkaz=S^ez@jSIZ50PHXPv(#3P5HsDCwGv6J=i5PO>i|XTS1Mf zdat8aYmQ@ULI;;WVCI;FnhcnI0myQGHi*GP)Bi#omJr8AFv3OsE0~b6D}~@M$DIZ3sEbwfm@AiDgjq+`OLgahE=CsDCskE z-Fk;WhZmIEZ&>E8Et=`MDCSbcu-{#@L)rY`^i{VnTz7^3&6#hn*OUNGGLkNV3%>BN zVrA7#;A4ewJnj^_*U=Y!iMVmFz1)jdEaEN;h*DcF@Yi!7j+9f7r25+kbJ!vZruzs< z7m7F)nnzlV=ocQRi<00iwuDJ6w{h;Lhw;(8O=)Q%RQc7xCUc%>G3V=t2ybfGo9JB? z*%H}-ph0vC;`%`bG6a=ltK#=}p6&&Nkx-#}ES{Ii5X=DYC^k zHjQvhgsky(4lZBbtjwEdRCHfg7qeE!u9STYg})R4EfQs&`0+l_1NcmykH>6;du$YD z5*y(j{A33AC|FDoZbL0c42FxxW^s&RXQO+6^H}|u;c{;>j&D2;2jj!>@%X;@>9|>o z_r)n4ui{vVcXU)p+1+E5`I1q_5~S1%K7R$VlXF?rmI=v-iBi6KS}3x>>a#k>(WBG= zh+0{vER6GzuVGv;F~5onU94B>8?9EG&lXqoY)v82kItUTxB~kWIH(LODB% zk-NWlExhvKoaPhb!-$3)0GO{x zp;=tfvxeH}OclbgsW42085p8M3Pd9mJXnPkTAHKK(i{cME%*YcGGmOCDM-)}HYEkG zKanP1grVXKNPi?j74d`IN%Ti&7fAZ7R>L1p|LQPrwGj@ZfAy@uU@y~Dwy9R8vy+pP z7aZ?R$^A0o3>=PaFM(ESvZ_BP*1|PPW+aIIAL1Vt#x3L4$HYe+kI9dQ_tp*wLd{q6 zZ<01SHid_zEsibW$0+{&&{UWj07HCemZsbJRAphOF)DtbqsvO?<} zh|C4Xm$F*XS2y(?aehS#IJSYeF<2wKiYk90yjm#Kx-V!tqUf-OJBEbJ`M?4aDf(JT zgp@;eIwfD&AMzED@=8SoiVq1`cco&Tsp7n9sB-}*0*h0U;#%0HK}riFv`~VS3qlGH z&4{0Nw^XrLRz|Ke1Q@2GP9FrwEpkb<9egw!-7q6IN>2I*I+lUhiH7 zD*kW{){wFT@nR7aq{4oZ{%r0mz_G%3fhr7k*BbD)1Cb{`NwS+qMsZd>!W)-Jqj)?9 z_7n|_^v=pj#6#?S_>nOfM}&iHYLb;`Z;i~r6@U&F{DFa5`GVzN|RW>FD0BG($R4~*zdsj7sv0b5jVrItdrp5a%;>i zYz<8B=(?bbvEW(t?lLPENx}y&`^3EW-@htaiu>ly%ulN6ncw&dD`yIeVs49Q*Zl4_ z2b9bYo*j8@fo!$9f|RCFg*iW)`S&{_MY|A>L$}jghS$y<@9$P%Jnpb~BbU+_?_I1q zVpv99g!He7WRx2|JzM*Fk@6wh*JWiO+w?>cX#fS!@pm*3i?%jtDWUZMHyDSPbAtn`Th4RoXv5Wv<+-7ZF#t7+G=Tye6u+Ity zt;5!F>ptsgtJyl?nWfYWC|K{kpvI7qM?6JDiyd?WGs_X}#R}(VG=LvfS1qEs!&g~6 zQ?D2N_ORklJD|h-;D__pbq9%RqV>lX6WB#G6H}s}0qafNEY0NYCpJw#@k4qYq|+U1 zSCm>hq=dT~cPwtvMcn(PEIBIM-nFQ8)S}i=xJ8fPqZZ9uJdYeVkC4zjLPGOaxbU~F zXq~sBrL$JFE`Tb3(S7*X6#~A@iYz%jOHR*rkqi^u&_#xPcHztrlAYHTz!&Z!rWQW5 ziz53ee2p##xrT#W!-2#3#x=AOQNE3H-!rbEwc%@^?Vsxwe5gh>_n$@7i>{-2e8rB1 zORyNw7Oz>W<70|z;i|P;*6vtKuU*G1ZdVdH3)`DB8&T;Z){@AEJ|<01;!jISVsiZ3 zYLf}DZ{VnuCFRQmU7mN`>b>al=;{{M%&u9xj#b(hI|wg0G!s!xSz>BsNn5sS9@&~l zw&txs&+#5%3r*Y5g^g9*++|p;r1A%{x2tQz3XB_Y4Y>JkGScUKJpK%}{!SW2?Sdy@NTj~Yun+$SE3v7Xh*z0qy%%K9`DS*O`S=k4U50&|<@|N!b)Rxy z`{44WBN2~H=)7pA*EuKR6-;VsUG>Hl6y@$&G}FGKW-&){t2(Qz^S<`wGjr-4egd{r zB6MY`Q=7$P+vZKTEnmL2=TkFV*J&PPO?su6qYvIUT+pjaEZLdm#Kc5O?lRQYt_O2n zGwzL@YCNvaSqne*NG?KvIg)=?0O!+C$Oh&YOT0+g!N_+EQA8ZmI?g2q^a*$joNG=h~5YJDBK=@1x zs%H$aNGgVC%wUNq;4?BHK*dW0g|zEDzTXZTp3+4^OVscM>3m#_cd-7Pkrz`e63353 zvSVr=1?jV-j)cXIWH^8>2%C=;J;XmvJizUwPHv#xxV{+Yh&JYkeBp?!;XD*>@{lGE zhnqZ}Y6S$zkwMZBBs+uTL2+xQC1f&K1mPnxnQG;K>j*+9xu>ePRkC~IEWf1t8|d_WJ03h6v|{m@numi6S6Oh1v8stfNexI3A0MqFwNM;_NsF_haAcFt?X`l{0bJDWqn~!FXFZ)0Y{{3};MZ zneoj23=J|;h9U!4c_bz>gB8Oul>Og3hGfM@Gp2-(_6DGtQOy1p^W)(IBj_LJP8fcn z4ptN>e}l6koJz4#$wtQ$WWc*9#4(VV?Hc-jI))Gr&u+W`HOg@Lmp`|BNb}e&?HA9? zaq4BkG;igtTP=3Xx~@g-Vz|Y*Q{P*@uJ=O~a;f(|!|UI)$g3s_WLh$UHkAyT{p= z*v*(-zCXQs`To93H$H0(Eqyj@T6)>W4KLz+Q(!_%_}nB`wevT88~EuCU;-|><6Y@p zb=D@*LjBDL!oyr(P)^vBR3eZR5=_z|x-<}kehqqfR8v?~bJ`>gsx(@;EnW@y%xKlL z_gf#5Z;7ZA(J7q2L3i9j-NM{1+-|>JzRh!sa!Y_4=pQgfy6^$XQB%?4#=#AX5!}Gh zQXrA^9>8+m2K)ZR5Z4Ug6K@U)e>S5Iot<7B0d>jE_yPrvfBMF)ukLvD_G`EQWOen% zi}!x!%1>Xvn11H*ozL8HcI=7!|MAm*zI9&x@lXA5=0ErS)0qbb0d?-rGt21b5iO=b zjaog67Bh25)^mr@h70Wc#{Pe&4`_78Nu;_{BT3I1_T;y4Ba!6V`AkNQPG@D4o$)_! zOvI&I5DY3L?4A7#n<8=@5V;N@qFyAk5rUi|BK#w%jg!*LNK_XIMPCT%cbq$W2oq|D zfN54CZh2u&54t--*G?jJotmLl7<^N|)l~u-HFQ*(v8Mp^+0o5d@ZI1WAbF0GA#36J z44&i76rL=m`cDgUFg4etrPAfnE{Ex!9GsJ@&smcRhTKiuHpjNyUECAwyWF37 zTWQXQa(`v0V$$cpB1fk)vJ=5O-#t+$0(q$zNJUnqLSO+UXVbK)Aaz1~ClrhC$*0&m z+QY)QKn)6G!ZQLbysuGCoT991S|3j98;d}k4ripmnInTeI4P2tTX+twW zJ?}xJ?Gnae#zalUDr@5sm83OUN>ten4Hj)^RpvW2*lsCke>G7PiOpfLdjPUbC&`Co zSbL$t{^rn--QxB6i2>)cHwW+-$&qxT>~4kajc8e_1)|EHSx$JfA%gMHo+j9$pg*~I z_p1A@7`b=&o69=W9p2jV8O_&~a=N8hNJ+p7Z~w&V&2ulkLf=p-#_8JV>)Wmzy6d&6 z$9A~I{LH%_D~A#Z=&`hKrmyNRDfS&R-`o=G*>K4<&;85DC5miBy{IFI_S5j^v$NRR z2<09#!kETLEICZfzA%A0VPa2_1EEAK9v^l;Dj~X}}_TV-o{)1~P?k8*BrPcsLx^ z;Fvb9Q9zTB{V}N>*UZ{r=E?Kf^@as8dU9m6IW55G)PSQoG6>9agpVQ|fQWIzqxgu* zSA?{+!8s4yNT&_pUnhfRp}Vt{1=+&z-t#e_k9eb2+_bH0NhKaz@0Q*9lGAqa+?nj6 zs82B4V*YSSfNuJkpZ;`yF4eivmHF7r(iJJB*yA38&ad8gQNZwrZ$5XD`Zq*V?WP-M z(NwBTXsWDZy-h(9<$)y114Y%(rL4F;6%ldR2JaKG2<^c8#delUi4l{WHN$OYIAn(A zL=i&R#`_{IUhqj}8nV?N~%5DhBXyHB3eb5gdE%nDm+i@LQr{L6P zm#2tdEcFu^LK!D^{0`^s&@SCFEW$x?SR5B=u>m`EU&`+*ggMCN_NL%qYB)8PqP0{h zMWvpH8t8!9v&&lygvb06pgoOq7|%uEK!2@>5XZzhh^jcjCR-h0KgMU z0yb?9Ktr97JP$HX^h5Ro=>x}yY12SIGV1XF;0?NOFWgJ*rT1DMwT-*S{p0F*U{Cas z*xtMq0jDg^48a*Mx}_K^#_!KP7Jn>f9_Yt#%8}N5H9qaDL0za(Xi<%HNz$ zJ~-6I${|#l7?Lf2HPqP{sPGZb-qZIMXe@v9cDzjEe3^ZCc_=v-NLNV1iFX6ALTM8N2~Eh;n{yqV}%v(s>3 zFyu~&W_7lrT~22(Fw2}Lx!?8a;{yYe(lP1iKr4oeh(Uc0*w78kv(`4jO%zoR?s4q# zz2tt$)9}6HWA_H(EM6wN< z$n1ZR5jir-OKj3E$+;qQTXx4BE<%xIhGcc(OwUd-?dr1EUoWR4a~-MJ*n+}_w!iPX zDeujgzB}`;i>Cii|J+Ras;kSJuBNVwc&=ZP{5YaN3R%_D^yfi>+Axd$JSpPu;+o+d zi_vD+|*;sBpYb3POoM6!`iann2qN^I0v_Ou; ze*`1`Ln1<&pG1TF1m^h#V$sVSm(&Q2YK-CcPRclfg%6Qi`4hN8s8p6y$=RN5w33K# z46i0J$<`6M5-^A`DH*L$g(>WG(o$Ie>`$30macG*wj{hnGy6H`Ds0n4R)rF&7?*o6yY%33txpMNK%btpFb|kea>+w zHHKFEowPAakwKt{Jd@ws@lmXZR2?GJUNUJmnb4F>ZiestJX%DmNm2u%BOv+%Kys*3 zFaU^C2~TDUiB&TZSG>fDoKP6SVh=Vr>jbiE2*qMI^7)8VN0-+etG37$!AG_lX%evm z$3W?;#fT@Ivd{cBzx7iKSB~TYT}$A+{(ANk%WE6y&rko`-o*h&Y~*`m7xzCf2KUVC zP+?;Fv9Z3+6%>0(7nQ)VD@4mvh?X_#z1edU@W3xKWL9ejS~Rp!^sk-;I7MOV)Tw$A zX{O=@uTQ%o@G8gi(Fi)v;vx=Bmy>a{5z|0sDAIx?hDItHzRt1ig?}S~wD{&R=}pr8 zyu4c2pj_^wePn93Y7}e3E8SI>&*hKtQ6b{c|uJFy&ViKY6sN&*?wF>x2WZE_0ONXT>9E%!n1099S!=kei z!J^tHWwRrrXUOc={%PfHAL0$=jDHH3qlz&NzQGe%Uhgzu-Gx~M{)b5fjA;T!TF8X3 zNEYIx+(UxqvDJ}~d*=u$`M3oqHl09dNx*5Kg0seH2ri+enOgu2rXlum>hk--d6uNRBn+UyxWS`Mfr(%cJ3CR%_PL1e9v1ar<wCVrUyS>k8uOw@dz3D+;Jh{5`;u79xo|A zm!kMG$(T>^3W!q$)DJJfQ;6L{@YI1Y6dia=0(OLpkY1Ifa9Gm-g&QHrp+gWmD=)ws zKmu^^NnK3q=w{>bv_(5Beq2CS;IjuO!N)T(JZi$NtHnMa+~nT$1OkVkoS=a;97~^0 zQ+VjZA#`ebA4{BneHl2GfjGvZW%SJc%w*0LLGi{_P1yGY8 zHK5(fNk4*dcxg?$?>91(FuZF`V_?*$49gJDLqg;Q&RODGf`gmg^1q&k1kXZZOg;+- zj37=H2^K6>l6dl2Y)at)D^4s?f_U!P^Mwl+rzgC%1TNfU>sfd0yyM-FF^J>nG-Y>$ zB4&^yx;!ibCXpsTLSlr0jf^EH&Ql_ihD=0a$eEVpX>P+9Z3rG49778J==Up1%F_!E zE(y7~c3gNEw}6cws$qLZ5&s4DJiz3NVbwDvE@|BI8LpNH%xh%)EI&-+OOnIiRS~uT8$Dy*Bc#Q{SBbt@fv9{(j^PR*Ly~eQs_v z!#nLHZO{5ewLPC57L%t=ou3Fq?{*#W|gVBGC4vxO^{77}==E&g4D|moC_PMF~zZ@5t zDHT~;h@pE_TxO{_k;$reG zxBG9ZysI8beUSz|ygr+eU&}L=`N5;-&Ygbej}ILkeri55_nDcKBaF`0!%p{n?p^M;;u-i1eUbM${<~Ze z4k|!!UN#gi`$?BJ+%L90=~g#0L#~^cBiHO^_kJEE+V8gOS}F&~6X{FL`jWaK`O;Sl zJstMk=UO}{^US`ivNI!DH2+ERIt&e;-V-CYSYyn>UTSG7g_vw0OF9k=lc zSe65+3yjbI2)H7PoQD+GCa>*D zR=&V7^O%G3IqL3_6uH%I>HNN@iWyq~!$XJ!%` z?;Tr>_B^=;cNifX(Vk6VWbq#3_N_B_`*?snUVR;&mvHaDy6JmXw*>pWvt53!aQ66} z*cJ9YRY@Ou`%n@?o!5JxwQP0Ub}B>ylb~l#kwM52nQAIrB zDsj6uBz4`NOCKNAo`%HB^iI>C(>)|QjN{II#xdCq+Gu+U89QZx#aGhV!{rt@nyI}b zK43kTE5F4aBs<>KzE#|WM4wljUMzbN7wX9`*=2h8Bs*CTqx726KP%$3LdL40{N8`h zHSZD8u=};9KK8PRcNFt)RWhYuu71xJ|7a;IBXy%`M=aBHhSwoxXWnKlPz6xg5+1rxZ6Q!kf^oy>C{Z^G&V`A9~S_b47)o2#7ODvD< zC;dxISGsmCDND1A5lJSQIapNY7WJ;-z6!n?>(8>!(%K69u!M2xOMxvQ%?rv`$w&CC zs}YJ3*;m%eDX+4OL6NQL;g`dG6_%6yWsKH&hp1v^o;|HGdIg0Ud0XaI&np?J9D75k z5(atDD|jt;u`~E8Z81{P`mx*K@gA4-FRQLOMy|>&8I2sb>KrQ?)m1DF_G7Qs4bH>@ zb7k4%S~o+MID4h_Np?yd)s!VtAH_CpOOn;%fEtlflJPYa>AP_u)U%8U*;VUmzL#Xw z<{RST`3sjty=99P-}B?$z!o__4E)fH9p76JS=SYYv)zg#Vc3x!-m$F(QFnIiNL1{O zF#H|UGwkiQYlew{t9yop8W%GwPBZi)e=D9{x5EhX#pQ)7mzf>h%)DA#sqdZdM8Xtt zXj=A;8QvED*1hfRcGl^V56zC_ZHwyGmeaJwIZ=yE&$V|kH*_N3)5W^ejQvoQ&Cs&F zSX{Vr>0;MN5w(NBb!=uC;+EE+=Ho)Ce zK-0Ujt}y~L4z$Gf4#g&Ppw;p_p6i=dKX|5u$_~jLAF6O|$3Z(5md##*r)9fAKcYxX zZ&$HXJRlJ8w48DRyM?s{+Eyy?Kl6pS210!Zu`I3Re1Wi6K~vUuS(qZxk~_bG)s z5OIeFf zgge<|{juTLa#=Nu#ZCA^oY84I0FNU~_jWc!B*URuBtfLk=ML*dpJbb2A~YGFU>7#m z_n)w;8E%81ay{vBB!F#d8-oP!%Hn=Ez7;NnuI z2wOANRT}l{HkytWYb%_dJDALHuFji8~D-*aoZv_ZMNK^ z8kN#n++1v*{F>O%QtbLYwu<4ZUeBPj5p_-Om}$parpP(RF8}cpc&(I91Ew$P-{~~R zQ#70tCaO$RQ3NWGY2Ve;o^VCNp9gj^yc`@CS%<6+s?)o`$`cz9fX-)QRDiirmCEIq zWO7p2+eiwdrFHK|E3gW&0(EXB;TUEODzH79c2SntOy?{e(>;ucFwlg$p;;e+isCC{ z(L2g7IP1tA1NP!SYIn(&e;|woZIC=IZC_a$9FR_W+{6+X9+VW6GyA>&i0PT|7a^1 z0p3Q`tN>C3P%l^hF<7{MW zWg=>4VQWUl@=y6?~YNTpTQ1>||^#9Gpx%ygVFatSs!j zOq`r7+&udKWRtS8HDe=VWqVUmMM&u1Ds%kbRQ@N&e-tPC2Qn(i%wk4PW`8mHzv{2* zV(k16)Wl^Z{u2F#knK$`83{FeX&DK5qwi$QGNx}ZSUG$Aqg&O(=?w%KJ9B%oH^f!U zEUezv!-I@Q^ldfFXpxx}9Zk&~t?VqwX#U0a)~V|9{kyFh;4hjiZ+b9mlIgOL>2h;( zGx4yqv9Rltv68WJld*BKkg@WR>9YN+|4mI!78ZRnHs1g0)F)%-dgEn#gPV(+m-~%| zi;IhimzRx|_ic!mjDw5pKZ6|HWV&3eEN^1|;>*Ls`^Ln=`rj09A~?C2SUK6)IsS|9 z-&6kA=zr#6c@xUZ!Oq0P!Og{`Psa6j%^d$~{=H(d|Fr(Q&d$!x#PYVdtpA|-OZUHN z`d{t;!sds{$~>Qe{1ufY5r3E{rv}le?s_XPZFB{!2Vxu!}%W@db2WT zGsic_u{ClwlQ1)}H#I|M{%U4t;cQ9v<_SVVWdGxG?wR^OChUS(Q2$&ANXD+^1;Wu} z1jq9&%M#s5n|MK`|74m*RQ`g|@f;}6Z

uTmQ^!_ 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�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 n9sJ%wa{w5GN>4APBK>~=5*W1= diff --git a/tiered_vs._tierless_programming/img/bar_chart.pdf b/tiered_vs._tierless_programming/img/bar_chart.pdf deleted file mode 100644 index 2abbc1bc8d377e01d4a8d30ea09322b8a9729ae8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20180 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 XZ%a>a!c+- delay) { +void blink(int pin, int dlay, long *lastrun, bool *st) { + if (millis() - *lastrun > dlay) { digitalWrite(pin, *st = !*st); - *lastrun += delay; + *lastrun += dlay; } } @@ -114,13 +115,14 @@ Furthermore, it is very difficult to represent dependencies between threads, oft \section{Blinking in \texorpdfstring{\gls{MTASK}}{mTask}} The \cleaninline{delay} \emph{task} does not block the execution but \emph{just} emits no value when the target waiting time has not yet passed and emits a stable value when the time is met. -In contrast, the \cleaninline{delay} \emph{function} on the \gls{ARDUINO} is blocking which prohibits interleaving. +In contrast, the \arduinoinline{delay()} \emph{function} on the \gls{ARDUINO} is blocking which prohibits interleaving. To make code reuse possible and make the implementation more intuitive, the blinking behaviour is lifted to a recursive function instead of using the imperative \cleaninline{rpeat} construct. The function is parametrized with the current state, the pin to blink and the waiting time. Creating recursive functions like this is not possible in the \gls{ARDUINO} language because the program would run out of stack in an instant and nothing can be interleaved. With a parallel combinator, tasks can be executed in an interleaved fashion. Therefore, blinking three different blinking patterns is as simple as combining the three calls to the \cleaninline{blink} function with their arguments as seen in \cref{lst:blinkthreadmtask}. +% VimTeX: SynIgnore on \begin{lstClean}[label={lst:blinkthreadmtask},caption={Threaded blinking.}] blinktask :: MTask v () | mtask v blinktask = @@ -135,8 +137,10 @@ blinktask = .||. blink (true, d2, lit 300) .||. blink (true, d3, lit 800) }\end{lstClean} +% VimTeX: SynIgnore off -\mychapter{chp:mtask_dsl}{The \texorpdfstring{\gls{MTASK}}{mTask} \texorpdfstring{\gls{DSL}}{DSL}} +\chapter{The \texorpdfstring{\gls{MTASK}}{mTask} \texorpdfstring{\gls{DSL}}{DSL}}% +\label{chp:mtask_dsl} \begin{chapterabstract} This chapter serves as a complete guide to the \gls{MTASK} language, from an \gls{MTASK} programmer's perspective. \end{chapterabstract} @@ -251,7 +255,8 @@ For convenience, there are many lower-cased macro definitions for often used con \Cref{lst:example_exprs} shows some examples of these expressions. \cleaninline{e0} defines the literal $42$, \cleaninline{e1} calculates the literal $42.0$ using real numbers. \cleaninline{e2} compares \cleaninline{e0} and \cleaninline{e1} as integers and if they are equal it returns the \cleaninline{e2}$/2$ and \cleaninline{e0} otherwise. -\cleaninline{approxEqual} performs an approximate equality---albeit not taking into account all floating point pecularities---and demonstrates that \gls{CLEAN} can be used as a macro language. +\cleaninline{approxEqual} performs an approximate equality---albeit not taking into account all floating point pecularities---and demonstrates that \gls{CLEAN} can be used as a macro language, i.e.\ maximise linguistic reuse~\cite{krishnamurthi_linguistic_2001}. +\todo{uitzoeken waar dit handig is} When calling \cleaninline{approxEqual} in an \gls{MTASK} function, the resulting code is inlined. \begin{lstClean}[label={lst:example_exprs},caption={Example \gls{MTASK} expressions.}] @@ -272,8 +277,6 @@ approxEqual x y eps = if' (x == y) true (x -. y < eps) ) \end{lstClean} -%jona -%jaunda \subsection{Data Types} Most of \gls{CLEAN}'s basic types have been mapped on \gls{MTASK} types. @@ -322,12 +325,13 @@ Many of the often used functions are already bundled in the \cleaninline{mtask} Definiting zero arity functions is shown in the \cleaninline{zeroarity} expression. Finally, \cleaninline{swapTuple} shows an example of a tuple being swapped. +% VimTeX: SynIgnore on \begin{lstClean}[label={lst:function_examples},caption={Function examples in \gls{MTASK}.}] factorial :: Main (v Int) | mtask v factorial = - fun \fac=(\i->if' (i <. lit 1) - (lit 1) - (i *. fac (i -. lit 1))) + fun \fac=(\i->if' (i <. lit 1) + (lit 1) + (i *. fac (i -. lit 1))) In {main = fac (lit 5) } factorialtail :: Main (v Int) | mtask v & fun (v Int, v Int) v @@ -349,6 +353,7 @@ swapTuple = fun \swap=(tupopen \(x, y)->tupl y x) In {main = swap (tupl true (lit 42)) } \end{lstClean} +% VimTeX: SynIgnore off \section{Tasks}\label{sec:top} \Gls{MTASK}'s task language can be divided into three categories, namely @@ -361,19 +366,29 @@ swapTuple = \end{enumerate*} As \gls{MTASK} is integrated with \gls{ITASK}, the same stability distinction is made for task values. +A task in \gls{MTASK} is denoted by the \gls{DSL} type synonym shown in \cref{lst:task_type}. + +\begin{lstClean}[label={lst:task_type},caption={Task type in \gls{MTASK}.}] +:: MTask v a :== v (TaskValue a) +:: TaskValue a + = NoValue + | Value a Bool +\end{lstClean} \subsection{Basic tasks} The most rudimentary basic tasks are the \cleaninline{rtrn} and \cleaninline{unstable} tasks. They lift the value from the \gls{MTASK} expression language to the task domain either as a stable value or an unstable value. +There is also a special type of basic task for delaying execution. +The \cleaninline{delay} task---given a number of milliseconds---yields an unstable value while the time has not passed. +Once the specified time has passed, the time it overshot the planned time is yielded as a stable task value. \begin{lstClean}[label={lst:basic_tasks},caption={Function examples in \gls{MTASK}.}] -class rtrn v :: (v t) -> MTask v t | type t -class unstable v :: (v t) -> MTask v t | type t +class rtrn v :: (v t) -> MTask v t +class unstable v :: (v t) -> MTask v t +class delay v :: (v n) -> MTask v n | long v n \end{lstClean} -\todo{examples} - -\subsubsection{Peripherals} +\subsubsection{Peripherals}\label{sssec:peripherals} For every sensor or actuator, basic tasks are available that allow interaction with the specific peripheral. The type classes for these tasks are not included in the \cleaninline{mtask} class collection as not all devices nor all language interpretations have such peripherals connected. \todo{Historically, peripheral support has been added \emph{by need}.} @@ -429,79 +444,208 @@ class dio p v | pin p where readD :: (v p) -> MTask v Bool | pin p class pinMode v where - pinMode :: (v PinMode) (v p) -> MTask v () | pin, type p - declarePin :: p PinMode ((v p) -> Main (v a)) -> Main (v a) | pin p & type a + pinMode :: (v PinMode) (v p) -> MTask v () | pin p + declarePin :: p PinMode ((v p) -> Main (v a)) -> Main (v a) | pin p \end{lstClean} -\todo{onder Task Combinators verplaatsen zodat de voorbeeldjes leuker kunnen zijn?} \subsection{Task combinators} -\subsubsection{Parallel}\label{sssec:combinators_parallel} -\begin{lstClean}[label={lst:mtask_parallel},caption{Parallel task combinators in \gls{MTASK}.}] -class (.&&.) infixr 4 v :: (MTask v a) (MTask v b) -> MTask v (a, b) | type a & type b -class (.||.) infixr 3 v :: (MTask v a) (MTask v a) -> MTask v a | type a -\end{lstClean} +Task combinators are used to combine multiple tasks into one to describe workflows. +There are three main types of task combinators, namely: +\begin{enumerate*} + \item Sequential combinators that execute tasks one after the other, possibly using the result of the left hand side. + \item Parallel combinators that execute tasks at the same time combining the result. + \item Miscellaneous combinators that change the semantics of a task---e.g.\ repeat it or delay execution. +\end{enumerate*} \subsubsection{Sequential} -\begin{lstClean}[label={lst:mtask_sequential},caption{Sequential task combinators in \gls{MTASK}.}] +Sequential task combination allows the right-hand side task to observe the left-hand side task value. +All seqential task combinators are expressed in the \cleaninline{expr} class and can be---and are by default---derived from the Swiss army knife step combinator \cleaninline{>>*.}. +This combinator has a single task on the left-hand side and a list of \emph{task continuations} on the right-hand side. +The list of task continuations is checked every rewrite step and if one of the predicates matches, the task continues with the result of this combination. +\cleaninline{>>=.} is a shorthand for the bind operation, if the left-hand side is stable, the right-hand side function is called to produce a new task. +\cleaninline{>>|.} is a shorthand for the sequence operation, if the left-hand side is stable, it continues with the right-hand side task. +The \cleaninline{>>~.} and \cleaninline{>>..} combinators are variants of the ones above that ignore the stability and continue on an unstable value as well. + +\begin{lstClean}[label={lst:mtask_sequential},caption={Sequential task combinators in \gls{MTASK}.}] class step v | expr v where - (>>*.) infixl 1 :: (MTask v t) [Step v t u] -> MTask v u | type u & type t - (>>=.) infixl 0 :: (MTask v t) ((v t) -> MTask v u) -> MTask v u | expr v & type u & type t - (>>|.) infixl 0 :: (MTask v t) (MTask v u) -> MTask v u | expr v & type u & type t - (>>|.) ma mb = ma >>*. [IfStable (\_->lit True) \_->mb] - (>>~.) infixl 0 :: (MTask v t) ((v t) -> MTask v u) -> MTask v u | expr v & type u & type t - (>>~.) ma amb = ma >>*. [IfValue (\_->lit True) amb] - (>>..) infixl 0 :: (MTask v t) (MTask v u) -> MTask v u | expr v & type u & type t - (>>..) ma mb = ma >>*. [IfValue (\_->lit True) \_->mb] + (>>*.) infixl 1 :: (MTask v t) [Step v t u] -> MTask v u + (>>=.) infixl 0 :: (MTask v t) ((v t) -> MTask v u) -> MTask v u + (>>|.) infixl 0 :: (MTask v t) (MTask v u) -> MTask v u + (>>~.) infixl 0 :: (MTask v t) ((v t) -> MTask v u) -> MTask v u + (>>..) infixl 0 :: (MTask v t) (MTask v u) -> MTask v u :: Step v t u = IfValue ((v t) -> v Bool) ((v t) -> MTask v u) | IfStable ((v t) -> v Bool) ((v t) -> MTask v u) | IfUnstable ((v t) -> v Bool) ((v t) -> MTask v u) - | Always (MTask v u) + | Always (MTask v u) +\end{lstClean} + +\todo{more examples step?} + +The following listing shows an example of a step in action. +The \cleaninline{readPinBin} function produces an \gls{MTASK} task that classifies the value of an analogue pin into four bins. +It also shows that the nature of embedding allows the host language to be used as a macro language. +Furthermore + +\begin{lstClean}[label={lst:mtask_readpinbin},caption={Read an analog pin and bin the value in \gls{MTASK}.}] +readPinBin :: Int -> Main (MTask v Int) | mtask v +readPinBin lim = declarePin PMInput A2 \a2-> + { main = readA a2 >>*. + [ IfValue (\x->x <. lim) (\_->rtrn (lit bin)) + \\ lim <-[64,128,192,256] + & bin <- [0..]]} \end{lstClean} -\subsubsection{Miscellaneous} -\begin{lstClean}[label={lst:mtask_misc},caption{Miscellaneous task combinators in \gls{MTASK}.}] +\subsubsection{Parallel}\label{sssec:combinators_parallel} +The result of a parallel task combination is a compound that actually executes the tasks at the same time. + +There are two types of parallel task combinators (see \cref{lst:mtask_parallel}). +The conjunction combinator (\cleaninline{.&&.}) combines the result by putting the values from both sides in a tuple. +The stability of the task depends on the stability of both children. +If both children are stable, the result is stable, otherwise the result is unstable. +The disjunction combinator (\cleaninline{.\|\|.}) combines the results by picking the leftmost, most stable task. +The semantics are easily described using \gls{CLEAN} functions shown in \cref{lst:semantics_con,lst:semantics_dis}. + +\begin{figure}[ht] + \centering + \begin{subfigure}[t]{.5\textwidth} + \begin{lstClean}[caption={Semantics of the\\conjunction combinator.},label={lst:semantics_con}] +con :: (TaskValue a) (TaskValue b) + -> TaskValue (a, b) +con NoValue r = NoValue +con l NoValue = NoValue +con (Value l ls) (Value r rs) + = Value (l, r) (ls && rs) + + \end{lstClean} + \end{subfigure}% + \begin{subfigure}[t]{.5\textwidth} + \begin{lstClean}[caption={Semantics of the\\disjunction combinator.},label={lst:semantics_dis}] +dis :: (TaskValue a) (TaskValue a) + -> TaskValue a +dis NoValue r = r +dis l NoValue = l +dis (Value l ls) (Value r rs) + | rs = Value r True + | otherwise = Value l ls + \end{lstClean} + \end{subfigure} +\end{figure} + +\begin{lstClean}[label={lst:mtask_parallel},caption={Parallel task combinators in \gls{MTASK}.}] +class (.&&.) infixr 4 v :: (MTask v a) (MTask v b) -> MTask v (a, b) +class (.||.) infixr 3 v :: (MTask v a) (MTask v a) -> MTask v a +\end{lstClean} + +\Cref{lst:mtask_parallel_example} gives an example of the parallel task combinator. +This program will read two pins at the same time, returning when one of the pins becomes \arduinoinline{HIGH}. +If the combinator was the \cleaninline{.&&.} instead, the type would be \cleaninline{MTask v (Bool, Bool)} and the task would only return when both pins have been \arduinoinline{HIGH} but not necessarily at the same time. + +\begin{lstClean}[label={lst:mtask_parallel_example},caption={Parallel task combinator example in \gls{MTASK}.}] +task :: MTask v Bool +task = + declarePin D0 PMInput \d0-> + declarePin D1 PMInput \d1-> + let monitor pin = readD pin >>*. [IfValue (\x->x) \x->rtrn x] + In {main = monitor d0 .||. monitor d1} +\end{lstClean} + +\subsubsection{Repeat} +The \cleaninline{rpeat} combinator executes the child task. +If a stable value is observed, the task is reinstated. +The functionality of \cleaninline{rpeat} can also be simulated by using functions and sequential task combinators and even made to be stateful as can be seen in \cref{lst:blinkthreadmtask}. + +\begin{lstClean}[label={lst:mtask_repeat},caption={Repeat task combinators in \gls{MTASK}.}] class rpeat v where - /** - * Executes the task until it yields a stable value, after which it is restarted. - * - * @param task - * @param the transformed task - */ - rpeat :: (MTask v a) -> MTask v a | type a - rpeat t :== rpeatEvery Default t - /** - * Executes the task until it yields a stable value, after which it is restarted at most once every given timing interval. - * - * @param restarting interval - * @param task - * @param the transformed task - */ - rpeatEvery :: (TimingInterval v) (MTask v a) -> MTask v a | type a - -/** - * The basic task that waits for a specified amount of time - * - * @var view - * @param number of milliseconds to wait - * @result task that yields - * - when the time has passed: a stable value with the number of milliseconds it overshot the waiting time - * - when the time has not passed: an unstable value with the number of milliseconds it still needs to wait - */ -class delay v :: (v n) -> MTask v Long | type n & long v n + rpeat :: (MTask v a) -> MTask v a +\end{lstClean} + +To demonstrate the combinator, \cref{lst:mtask_repeat_example} show \cleaninline{rpeat} in use. +This task will mirror the value read from analog \gls{GPIO} pin \cleaninline{A1} to pin \cleaninline{A2} by constantly reading the pin and writing the result. + +\begin{lstClean}[label={lst:mtask_repeat_example},caption={Exemplatory repeat task in \gls{MTASK}.}] +task :: MTask v Int | mtask v +task = + declarePin A1 PMInput \a1-> + declarePin A2 PMOutput \a2-> + {main = rpeat (readA a1 >>~. writeA a2)} \end{lstClean} \subsection{\texorpdfstring{\Acrlongpl{SDS}}{Shared data sources}} +\Glspl{SDS} in \gls{MTASK} are by default references to shared memory. +Similar to peripherals (see \cref{sssec:peripherals}), they are constructed at the top level and are accessed through interaction tasks. +The \cleaninline{getSds} task yields the current value of the \gls{SDS} as an unstable value. +This behaviour is similar to the \cleaninline{watch} task in \gls{ITASK}. +Writing a new value to an \gls{SDS} is done using \cleaninline{setSds}. +This task yields the written value as a stable result after it is done writing. +Getting and immediately after setting an \gls{SDS} is not an \emph{atomic} operation. +It is possible that another task accesses the \gls{SDS} in between. +To circumvent this issue, \cleaninline{updSds} is created, this task atomically updates the value of the \gls{SDS}. + +\begin{lstClean}[label={lst:mtask_sds},caption={\Glspl{SDS} in \gls{MTASK}.}] +:: Sds a // abstract +class sds v where + sds :: ((v (Sds t)) -> In t (Main (MTask v u))) -> Main (MTask v u) + getSds :: (v (Sds t)) -> MTask v t + setSds :: (v (Sds t)) (v t) -> MTask v t + updSds :: (v (Sds t)) ((v t) -> v t) -> MTask v t +\end{lstClean} -\mychapter{chp:green_computing_mtask}{Green computing with \texorpdfstring{\gls{MTASK}}{mTask}} +\todo{examples sdss} + +\chapter{Green computing with \texorpdfstring{\gls{MTASK}}{mTask}}% +\label{chp:green_computing_mtask} + +\chapter{Integration with \texorpdfstring{\gls{ITASK}}{iTask}}% +\label{chp:integration_with_itask} +The \gls{MTASK} language is a multi-view \gls{DSL}, i.e.\ there are multiple interpretations possible for a single \gls{MTASK} expression. +Using the byte code compiler (\cleaninline{BCInterpret}) \gls{DSL} interpretation, \gls{MTASK} tasks can be fully integrated in \gls{ITASK} and executed as if they were regular \gls{ITASK} tasks, sharing data using \gls{ITASK} \glspl{SDS}. + +\Cref{fig:mtask_integration} shows the + +\begin{figure}[ht] + \centering + \includestandalone{mtask_integration} + \caption{\Gls{MTASK}'s integration with \gls{ITASK}.}% + \label{fig:mtask_integration} +\end{figure} -\mychapter{chp:integration_with_itask}{Integration with \texorpdfstring{\gls{ITASK}}{iTask}} \section{Devices} +\Gls{MTASK} tasks in the byte code compiler view are always executed on a certain device. +All communication with this device happens through a so-called \emph{channels} \gls{SDS}. +The channels contain three fields, a queue of messages that are received, a queue of messages to send and a stop flag. +Every communication method that implements the \cleaninline{channelSync} class can provide the communication with an \gls{MTASK} device. +As of now, serial port communication, direct \gls{TCP} communication and \gls{MQTT} over \gls{TCP} are supported as communication providers. +The \cleaninline{withDevice} function transforms a communication provider and a task that does something with this device to an \gls{ITASK} task. +This task sets up the communication, exchanges specifications, handles errors and cleans up after closing. +\Cref{lst:mtask_device} shows the types and interface to connecting devices. + +\begin{lstClean}[label={lst:mtask_device},caption={Device communication interface in \gls{MTASK}.}] +:: MTDevice //abstract +:: Channels :== ([MTMessageFro], [MTMessageTo], Bool) + +class channelSync a :: a (sds () Channels Channels) -> Task () | RWShared sds + +withDevice :: (a (MTDevice -> Task b) -> Task b) | iTask b & channelSync, iTask a + +\end{lstClean} + \section{Lifting tasks} +Once the connection with the device is established, \ldots +\begin{lstClean} +liftmTask :: (Main (BCInterpret (TaskValue u))) MTDevice -> Task u | iTask u +\end{lstClean} + \section{Lifting \texorpdfstring{\acrlongpl{SDS}}{shared data sources}} +\begin{lstClean}[label={lst:mtask_itasksds},caption={Lifted \gls{ITASK} \glspl{SDS} in \gls{MTASK}.}] +class liftsds v where + liftsds :: ((v (Sds t))->In (Shared sds t) (Main (MTask v u))) + -> Main (MTask v u) | RWShared sds +\end{lstClean} -\mychapter{chp:implementation}{Implementation} +\chapter{Implementation}% +\label{chp:implementation} IFL19 paper, bytecode instructieset~\cref{chp:bytecode_instruction_set} \section{Integration with \texorpdfstring{\gls{ITASK}}{iTask}} @@ -509,11 +653,11 @@ IFL18 paper stukken \chapter{\texorpdfstring{\gls{MTASK}}{mTask} history} \section{Generating \texorpdfstring{\gls{C}/\gls{CPP}}{C/C++} code} -A first throw at a class-based shallowly \gls{EDSL} for microprocessors was made by Pieter Koopman and Rinus Plasmijer in 2016~\citep{plasmeijer_shallow_2016}. +A first throw at a class-based shallowly \gls{EDSL} for microprocessors 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. There was no support for tasks or even functions. -Some time later in the 2015 CEFP summer school, an extended version was created that allowed the creation of imperative tasks, \glspl{SDS} and the usage of functions~\citep{koopman_type-safe_2019}. +Some time later in the 2015 \gls{CEFP} summer school, an extended version was created that allowed the creation of imperative tasks, \glspl{SDS} and the usage of functions~\citep{koopman_type-safe_2019}. The name then changed from \gls{ARDSL} to \gls{MTASK}. \section{Integration with \texorpdfstring{\gls{ITASK}}{iTask}} @@ -523,7 +667,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 Matheus Amazonas Cabral de Andrade that it was possible to build real-life \gls{IOT} systems with this integration~\citep{amazonas_cabral_de_andrade_developing_2018}. -Moreover, a course on the \gls{MTASK} simulator was provided at the 2018 CEFP/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}/\gls{3COWS} winter school in Ko\v{s}ice, Slovakia~\citep{koopman_simulation_2018}. \section{Transition to \texorpdfstring{\gls{TOP}}{TOP}} The \gls{MTASK} language as it is now was introduced in 2018~\citep{koopman_task-based_2018}. @@ -531,7 +675,7 @@ This paper updated the language to support functions, tasks and \glspl{SDS} but Later the bytecode 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 \gls{MCU} 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 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} summer school in Budapest, Hungary hosted a course on developing \gls{IOT} applications with \gls{MTASK} as well~\citep{lubbers_writing_2019}. \section{\texorpdfstring{\gls{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 (the lecture notes are to be written). diff --git a/mtask/mtask_by_example.tex b/top/mtask_by_example.tex similarity index 86% rename from mtask/mtask_by_example.tex rename to top/mtask_by_example.tex index c3d64ac..5accbeb 100644 --- a/mtask/mtask_by_example.tex +++ b/top/mtask_by_example.tex @@ -5,7 +5,8 @@ \pagenumbering{arabic} }{} -\mychapter{chp:mtask_by_example}{mTask by example} +\chapter{mTask by example}% +\label{chp:mtask_by_example} Hiermee beginnen, dan is het gelijk duidelijk hoe mTask werkt, dan zijn de volgende chapters implementatie dingen. CEFP19 lecture notes. Dit evt.\ ook gecombineerd met energy aware \gls{TOP} programming en CEFP22 lecture notes. diff --git a/introduction/hyponymy_of_dsls.tex b/top/mtask_integration.tex similarity index 100% rename from introduction/hyponymy_of_dsls.tex rename to top/mtask_integration.tex diff --git a/tvt/.chktexrc b/tvt/.chktexrc new file mode 100644 index 0000000..e62adec --- /dev/null +++ b/tvt/.chktexrc @@ -0,0 +1,24 @@ +CmdLine { + -v +} +VerbEnvir { + lstinline lstlisting algorithm code spec lstClean lstHaskell lstHaskellLhstex lstArduino lstPython +} +WipeArg { + \cinline:{} + \cleaninline:{} + \pythoninline:{} + \haskellinline:{} + \haskelllshtexinline:{} + \arduinoinline:{} + \texttt:{} + \url:{} + \only:{} + \orcid:{} +} +Silent { + \pause +} +MathEnvir { + code +} diff --git a/tiered_vs._tierless_programming/img/TempHistory.png b/tvt/img/TempHistory.png similarity index 100% rename from tiered_vs._tierless_programming/img/TempHistory.png rename to tvt/img/TempHistory.png diff --git a/tiered_vs._tierless_programming/img/TempHistory1.png b/tvt/img/TempHistory1.png similarity index 100% rename from tiered_vs._tierless_programming/img/TempHistory1.png rename to tvt/img/TempHistory1.png diff --git a/tiered_vs._tierless_programming/img/TempHistory2.png b/tvt/img/TempHistory2.png similarity index 100% rename from tiered_vs._tierless_programming/img/TempHistory2.png rename to tvt/img/TempHistory2.png diff --git a/tiered_vs._tierless_programming/img/arch.drawio b/tvt/img/arch.drawio similarity index 100% rename from tiered_vs._tierless_programming/img/arch.drawio rename to tvt/img/arch.drawio diff --git a/tiered_vs._tierless_programming/img/cwss_web.png b/tvt/img/cwss_web.png similarity index 100% rename from tiered_vs._tierless_programming/img/cwss_web.png rename to tvt/img/cwss_web.png diff --git a/tiered_vs._tierless_programming/img/cwts.png b/tvt/img/cwts.png similarity index 100% rename from tiered_vs._tierless_programming/img/cwts.png rename to tvt/img/cwts.png diff --git a/tiered_vs._tierless_programming/img/cwtsDiagram.png b/tvt/img/cwtsDiagram.png similarity index 100% rename from tiered_vs._tierless_programming/img/cwtsDiagram.png rename to tvt/img/cwtsDiagram.png diff --git a/tiered_vs._tierless_programming/img/cwtsDiagram2.png b/tvt/img/cwtsDiagram2.png similarity index 100% rename from tiered_vs._tierless_programming/img/cwtsDiagram2.png rename to tvt/img/cwtsDiagram2.png diff --git a/tiered_vs._tierless_programming/img/devTaskDiagram.png b/tvt/img/devTaskDiagram.png similarity index 100% rename from tiered_vs._tierless_programming/img/devTaskDiagram.png rename to tvt/img/devTaskDiagram.png diff --git a/tiered_vs._tierless_programming/img/devTaskDiagram2.png b/tvt/img/devTaskDiagram2.png similarity index 100% rename from tiered_vs._tierless_programming/img/devTaskDiagram2.png rename to tvt/img/devTaskDiagram2.png diff --git a/tiered_vs._tierless_programming/img/gen_stacked_barchart.py b/tvt/img/gen_stacked_barchart.py similarity index 100% rename from tiered_vs._tierless_programming/img/gen_stacked_barchart.py rename to tvt/img/gen_stacked_barchart.py diff --git a/tiered_vs._tierless_programming/img/iTaskTemp.png b/tvt/img/iTaskTemp.png similarity index 100% rename from tiered_vs._tierless_programming/img/iTaskTemp.png rename to tvt/img/iTaskTemp.png diff --git a/tiered_vs._tierless_programming/img/iTaskTemp1.png b/tvt/img/iTaskTemp1.png similarity index 100% rename from tiered_vs._tierless_programming/img/iTaskTemp1.png rename to tvt/img/iTaskTemp1.png diff --git a/tiered_vs._tierless_programming/img/iTaskTemp2.png b/tvt/img/iTaskTemp2.png similarity index 100% rename from tiered_vs._tierless_programming/img/iTaskTemp2.png rename to tvt/img/iTaskTemp2.png diff --git a/tiered_vs._tierless_programming/img/iTaskTempRemote.png b/tvt/img/iTaskTempRemote.png similarity index 100% rename from tiered_vs._tierless_programming/img/iTaskTempRemote.png rename to tvt/img/iTaskTempRemote.png diff --git a/tiered_vs._tierless_programming/img/mTaskTemp.png b/tvt/img/mTaskTemp.png similarity index 100% rename from tiered_vs._tierless_programming/img/mTaskTemp.png rename to tvt/img/mTaskTemp.png diff --git a/tiered_vs._tierless_programming/img/prss.jpg b/tvt/img/prss.jpg similarity index 100% rename from tiered_vs._tierless_programming/img/prss.jpg rename to tvt/img/prss.jpg diff --git a/tiered_vs._tierless_programming/img/readTempTask.png b/tvt/img/readTempTask.png similarity index 100% rename from tiered_vs._tierless_programming/img/readTempTask.png rename to tvt/img/readTempTask.png diff --git a/tiered_vs._tierless_programming/img/sensordata.png b/tvt/img/sensordata.png similarity index 100% rename from tiered_vs._tierless_programming/img/sensordata.png rename to tvt/img/sensordata.png diff --git a/tiered_vs._tierless_programming/img/simpleTempSensor.png b/tvt/img/simpleTempSensor.png similarity index 100% rename from tiered_vs._tierless_programming/img/simpleTempSensor.png rename to tvt/img/simpleTempSensor.png diff --git a/tiered_vs._tierless_programming/img/todolist1.png b/tvt/img/todolist1.png similarity index 100% rename from tiered_vs._tierless_programming/img/todolist1.png rename to tvt/img/todolist1.png diff --git a/tiered_vs._tierless_programming/img/todolist3.png b/tvt/img/todolist3.png similarity index 100% rename from tiered_vs._tierless_programming/img/todolist3.png rename to tvt/img/todolist3.png diff --git a/tiered_vs._tierless_programming/img/todolist4.png b/tvt/img/todolist4.png similarity index 100% rename from tiered_vs._tierless_programming/img/todolist4.png rename to tvt/img/todolist4.png diff --git a/tiered_vs._tierless_programming/img/todolist5.png b/tvt/img/todolist5.png similarity index 100% rename from tiered_vs._tierless_programming/img/todolist5.png rename to tvt/img/todolist5.png diff --git a/tiered_vs._tierless_programming/img/todolist6.png b/tvt/img/todolist6.png similarity index 100% rename from tiered_vs._tierless_programming/img/todolist6.png rename to tvt/img/todolist6.png diff --git a/tiered_vs._tierless_programming/img/todolist7.png b/tvt/img/todolist7.png similarity index 100% rename from tiered_vs._tierless_programming/img/todolist7.png rename to tvt/img/todolist7.png diff --git a/tiered_vs._tierless_programming/img/web_iot.png b/tvt/img/web_iot.png similarity index 100% rename from tiered_vs._tierless_programming/img/web_iot.png rename to tvt/img/web_iot.png diff --git a/tiered_vs._tierless_programming/img/wemos.jpg b/tvt/img/wemos.jpg similarity index 100% rename from tiered_vs._tierless_programming/img/wemos.jpg rename to tvt/img/wemos.jpg diff --git a/tiered_vs._tierless_programming/smart_campus.tex b/tvt/tvt.tex similarity index 77% rename from tiered_vs._tierless_programming/smart_campus.tex rename to tvt/tvt.tex index e1fbe34..737088b 100644 --- a/tiered_vs._tierless_programming/smart_campus.tex +++ b/tvt/tvt.tex @@ -5,8 +5,8 @@ \pagenumbering{arabic} }{} -\mychapter{chp:smart_campus}{Smart campus application} -%\title{Could Tierless Languages Reduce IoT Development Grief?} +\chapter{Could Tierless Languages Reduce IoT Development Grief?}% +\label{chp:smart_campus} \begin{chapterabstract} \Gls{IOT} software is notoriously complex, conventionally comprising multiple tiers. @@ -14,11 +14,11 @@ We report a systematic comparative evaluation of two tierless language technologies for \gls{IOT} stacks: one for resource-rich sensor nodes (\gls{CLEAN} with \gls{ITASK}), and one for resource-constrained sensor nodes (\gls{CLEAN} with \gls{ITASK} and \gls{MTASK}). The evaluation is based on four implementations of a typical smart campus application: two tierless and two \gls{PYTHON}-based tiered. - %, with two targeting microcontrollers and two targeting supersensors. + %, with two targeting microprocessors 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 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 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 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*} \end{chapterabstract} @@ -30,41 +30,40 @@ Conventional \gls{IOT} software stacks are notoriously complex and pose very sig 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. \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 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. - \item To ensure correctness the developer must maintain type safety across a range of very different languages and diverse type systems. - \item The developer must deal with the potentially diverse failure modes of each component, and of component interoperations. + \item To ensure correctness the developer must maintain type safety across a range of very different languages and diverse type systems. + \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/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}. -\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. +\Gls{IOT} sensor nodes may be microprocessors 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. Potentially a tierless language both reduces the development effort and improves correctness as correct interoperation and communication is automatically generated by the compiler. A tierless language may, however, introduce other problems. How expressive is the language? That is, can it readily express the required functionality? How maintainable is the software? Is the generated code efficient in terms of time, space, and power? -This paper reports a systematic comparative evaluation of two tierless language technologies for \gls{IOT} stacks: one targeting resource-constrained microcontrollers, and the other resource-rich supersensors. The basis of the comparison is four implementations of a typical smart campus \gls{IOT} stack~\citep{hentschel_supersensors:_2016}. Two implementations are conventional tiered \gls{PYTHON}-based stacks: \gls{PRS} and \gls{PWS}. The other two implementations are tierless: \gls{CRS} and \gls{CWS}. Our work makes the following research contributions, and the key results are summarised, discussed, and quantified in \cref{sec_t4t:Conclusion}. +This paper reports a systematic comparative evaluation of two tierless language technologies for \gls{IOT} stacks: one targeting resource-constrained microprocessors, and the other resource-rich supersensors. The basis of the comparison is four implementations of a typical smart campus \gls{IOT} stack~\citep{hentschel_supersensors:_2016}. Two implementations are conventional tiered \gls{PYTHON}-based stacks: \gls{PRS} and \gls{PWS}. The other two implementations are tierless: \gls{CRS} and \gls{CWS}. Our work makes the following research contributions, and the key results are summarised, discussed, and quantified in \cref{sec_t4t:Conclusion}. \begin{description} - \item[C1] We show that \emph{tierless languages have the potential to significantly reduce the development effort for \gls{IOT} systems}. - We systematically compare code size (\gls{SLOC}) of the four smart campus implementations as a measure of development effort and maintainability~\citep{alpernaswonderful,rosenberg1997some}. - The tierless implementations require 70\% less code than the tiered implementations. We analyse the codebases to attribute the code reduction to three factors. - \begin{enumerate*} - \item Tierless languages benefit from reduced interoperation, requiring far fewer languages, paradigms and source code files e.g.\ \gls{CWS} uses two languages, one paradigm and three source code files where \gls{PWS} uses seven languages, two paradigms and 35 source code files - %\jscomment{check out \url{https://editorsmanual.com/articles/numerals-vs-words-for-numbers/} - do we agree with this?} + \item[C1] We show that \emph{tierless languages have the potential to significantly reduce the development effort for \gls{IOT} systems}. + We systematically compare code size (\gls{SLOC}) of the four smart campus implementations as a measure of development effort and maintainability~\citep{alpernaswonderful,rosenberg1997some}. + The tierless implementations require 70\% less code than the tiered implementations. We analyse the codebases to attribute the code reduction to three factors. + \begin{enumerate*} + \item Tierless languages benefit from reduced interoperation, requiring far fewer languages, paradigms and source code files e.g.\ \gls{CWS} uses two languages, one paradigm and three source code files where \gls{PWS} uses seven languages, two paradigms and 35 source code files (\cref{table_t4t:multi,table_t4t:languages,table_t4t:paradigms}). - \item Tierless languages benefit from automatically synthesized, and hence correct, communication between components that may be distributed. - \item Tierless languages benefit from high-level programming abstractions like compositional and higher-order task combinators (\cref{sec_t4t:ProgrammingComparison}). - \end{enumerate*} + \item Tierless languages benefit from automatically synthesized, and hence correct, communication between components that may be distributed. + \item Tierless languages benefit from high-level programming abstractions like compositional and higher-order task combinators (\cref{sec_t4t:ProgrammingComparison}). + \end{enumerate*} \item[C2] We show that \emph{tierless languages have the potential to significantly improve the reliability of \gls{IOT} systems}. We demonstrate how tierless languages preserve type safety, improve maintainability and provide high-level failure management. For example, we illustrate a loss of type safety in \gls{PRS}. We also critique current tool and community support (\cref{sec_t4t:Discussion}). - \item[C3] We report \emph{the first comparison of a tierless \gls{IOT} codebase for resource-rich sensor nodes with one for resource-constrained sensor nodes}. The tierless smart campus implementations have a very similar code size (within 7\%), as do the tiered implementations. This suggests that the development and maintenance effort of simple tierless \gls{IOT} systems for resource-constrained and for resource-rich sensor nodes is similar, as it is for tiered technologies. The percentages of code required to implement each \gls{IOT} functionality in the tierless \gls{CLEAN} implementations is very similar, as it is in the tiered \gls{PYTHON} implementations. This suggests that the code for resource-constrained and resource-rich sensor nodes is broadly similar in tierless technologies, as in tiered technologies (\cref{sec_t4t:resourcerich}) + \item[C3] We report \emph{the first comparison of a tierless \gls{IOT} codebase for resource-rich sensor nodes with one for resource-constrained sensor nodes}. The tierless smart campus implementations have a very similar code size (within 7\%), as do the tiered implementations. This suggests that the development and maintenance effort of simple tierless \gls{IOT} systems for resource-constrained and for resource-rich sensor nodes is similar, as it is for tiered technologies. The percentages of code required to implement each \gls{IOT} functionality in the tierless \gls{CLEAN} implementations is very similar, as it is in the tiered \gls{PYTHON} implementations. This suggests that the code for resource-constrained and resource-rich sensor nodes is broadly similar in tierless technologies, as in tiered technologies (\cref{sec_t4t:resourcerich}) \item[C4] \emph{We present 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}).} - We show that the bare metal execution environment enforces some restrictions on \gls{MTASK} although they remain high level. Moreover, the environment conveys some advantages, e.g.\ better control over timing (\cref{sec_t4t:ComparingTierless}). + We show that the bare metal execution environment enforces some restrictions on \gls{MTASK} although they remain high level. Moreover, the environment conveys some advantages, e.g.\ better control over timing (\cref{sec_t4t:ComparingTierless}). \end{description} The current work extends~\citep{lubbers_tiered_2020} as follows. Contributions C3 and C4 are entirely new, and C1 is enhanced by being based on the analysis of four rather than two languages and implementations. @@ -97,10 +96,21 @@ Web applications are necessarily complex distributed systems, with client browse are even more complex as they combine a web application with a second distributed system of sensor and actuator nodes that collect and aggregate data, operate on it, and communicate with the server. Both web and \gls{IOT} applications are commonly structured into tiers, e.g.\ the classical four-tier Linux, Apache, MySQL and PHP (LAMP) stack. -\Gls{IOT} stacks typically have more tiers than webapps, with the number depending on the complexity of the application~\citep{sethi2017internet}. While other tiers, like the business layer~\citep{muccini2018iot} may be added above them, the focus of our study is on programming the lower four tiers of the \gls{PRS}, \gls{CRS}, \gls{PWS} and \gls{CWS} stacks, as illustrated in \cref{fig_t4t:iot_arch}. +\Gls{IOT} stacks typically have more tiers than webapps, with the number depending on the complexity of the application~\citep{sethi2017internet}. While other tiers, like the business layer~\citep{muccini2018iot} may be added above them, the focus of our study is on programming the lower four tiers of the \gls{PRS}, \gls{CRS}, \gls{PWS} and \gls{CWS} stacks, as illustrated in \cref{fig_t4t:iot_arch}. + +\begin{description}%[style=nextline] + \item [Perception layer] collects the data, interacts with the environment, and consists of devices using light, sound, motion, air quality and temperature sensors. + \item [Network layer] replays the communication messages between the sensor nodes and the server through protocols such as \gls{MQTT}. + + \item [Application layer] acts as the interface between the presentation layer and the perception layer, storing and processing the data. + + \item [Presentation layer] utilises web components as the interface between the human and devices where application services are provided. + +\end{description} + \begin{landscape} - \begin{figure}[ht] + \begin{figure} \includegraphics[width=\linewidth]{arch} \caption{% \gls{PRS} and \gls{PWS} (left) together with \gls{CRS} and \gls{PRS} (right) mapped to the four-tier \gls{IOT} architecture. @@ -111,59 +121,41 @@ Both web and \gls{IOT} applications are commonly structured into tiers, e.g.\ th \label{fig_t4t:iot_arch} \end{figure} \end{landscape} -% -\begin{enumerate} - - \item Perception Layer {--} collects the data, interacts with the environment, and consists of devices using light, sound, motion, air quality and temperature sensors. - \item Network Layer {--} replays the communication messages between the sensor nodes and the server through protocols such as \gls{MQTT}. - - \item Application Layer {--} acts as the interface between the presentation layer and the perception layer, storing and processing the data. - - \item Presentation Layer {--} utilises web components as the interface between the human and devices where application services are provided. - -\end{enumerate} - \subsection{The benefits and challenges of developing tiered \texorpdfstring{\gls{IOT}}{IoT} stacks} Using multiple tiers to structure complex software is a common software engineering practice that provides significant architectural benefits for \gls{IOT} and other software. The tiered \gls{PYTHON} \gls{PRS} and \gls{PWS} stacks exhibit these benefits. -\begin{enumerate} +\begin{description} - \item Modularity: tiers allow a system to be structured as a set of components with clearly defined functionality. They can be implemented independently, and may be interchanged with other components that have similar functionality~\citep{maccormack2007impact}. In \gls{PRS} and \gls{PWS}, for example, a different NoSQL DBMS could relatively easily be substituted for {MongoDB} + \item[Modularity] tiers allow a system to be structured as a set of components with clearly defined functionality. They can be implemented independently, and may be interchanged with other components that have similar functionality~\citep{maccormack2007impact}. In \gls{PRS} and \gls{PWS}, for example, a different NoSQL DBMS could relatively easily be substituted for {MongoDB}. - \item Abstraction: the hierarchical composition of components in the stack abstracts the view of the system as a whole. Enough detail is provided to understand the roles of each layer and how the components relate to one another~\citep{belle2013layered}. \Cref{fig_t4t:iot_arch} illustrates the abstraction of \gls{PRS} and \gls{PWS} into four tiers. + \item[Abstraction] the hierarchical composition of components in the stack abstracts the view of the system as a whole. Enough detail is provided to understand the roles of each layer and how the components relate to one another~\citep{belle2013layered}. \Cref{fig_t4t:iot_arch} illustrates the abstraction of \gls{PRS} and \gls{PWS} into four tiers. - \item Cohesion: well-defined boundaries ensure each tier contains functionality directly related to the task of the component~\citep{lee2001component}. The tiers in \gls{PRS} and \gls{PWS} contain all the functionality associated with perception, networking, application and presentation respectively. + \item[Cohesion] well-defined boundaries ensure each tier contains functionality directly related to the task of the component~\citep{lee2001component}. The tiers in \gls{PRS} and \gls{PWS} contain all the functionality associated with perception, networking, application and presentation respectively. -\end{enumerate} +\end{description} However, a tiered architecture poses significant challenges for developers of \gls{IOT} and other software. The tiered \gls{PYTHON} \gls{PRS} and \gls{PWS} stacks exhibit these challenges, and we analyse these in detail later in the paper. -\begin{enumerate} - - \item Polyglot Development {--} the developer must be fluent in all the languages and components in the stack, known as being a full stack developer for webapps~\citep{mazzei2018full}. That is, the developer must correctly use multiple languages that have different paradigms, i.e.\ manage significant \emph{semantic friction}~\citep{ireland2009classification}. For example the \gls{PWS} developer must integrate components written in seven languages with two paradigms (\cref{sec_t4t:interoperation}). - - \item Correct Interoperation {--} the developer must adhere to the \gls{API} or communication protocols between components. \Cref{sec_t4t:codesize,sec_t4t:resourcerich} show that communication requires some 17\% of \gls{PRS} and \gls{PWS} code, so around 100 \gls{SLOC}. \Cref{sec_t4t:Communication} discusses the complexity of writing this distributed communication code. - - \item Maintaining Type Safety {--} is a key element of the semantic friction encountered in multi-language stacks, and crucial for correctness. The developer must maintain type safety across a range of very different languages and diverse type systems, with minimal tool support. We show an example where \gls{PRS} loses type safety over the network layer (\Cref{sec_t4t:typesafety}). - - - \item Managing multiple failure modes {--} different components may have different failure modes, and these must be coordinated. \Cref{sec_t4t:NetworkManagement} outlines how \gls{PRS} and \gls{PWS} use heartbeats to manage failures. -\end{enumerate} - +\begin{description} + \item[Polyglot development] the developer must be fluent in all the languages and components in the stack, known as being a full-stack developer for webapps \citep{mazzei2018full}. That is, the developer must correctly use multiple languages that have different paradigms, i.e.\ manage significant \emph{semantic friction}~\citep{ireland2009classification}. For example the \gls{PWS} developer must integrate components written in seven languages with two paradigms (\cref{sec_t4t:interoperation}). + \item[Correct interoperation] the developer must adhere to the \gls{API} or communication protocols between components. \Cref{sec_t4t:codesize,sec_t4t:resourcerich} show that communication requires some 17\% of \gls{PRS} and \gls{PWS} code, so around 100 \gls{SLOC}. \Cref{sec_t4t:Communication} discusses the complexity of writing this distributed communication code. + \item[Maintaining type safety] is a key element of the semantic friction encountered in multi-language stacks, and crucial for correctness. The developer must maintain type safety across a range of very different languages and diverse type systems, with minimal tool support. We show an example where \gls{PRS} loses type safety over the network layer (\Cref{sec_t4t:typesafety}). + \item[Managing multiple failure modes] different components may have different failure modes, and these must be coordinated. \Cref{sec_t4t:NetworkManagement} outlines how \gls{PRS} and \gls{PWS} use heartbeats to manage failures. +\end{description} -Beyond \gls{PRS} and \gls{PWS} the challenges of tiered polyglot software development are evidenced in real world studies. As recent examples, a study of GitHub open source projects found an average of five different languages in each project, with many using tiered architectures~\citep{mayer2017multi}. +Beyond \gls{PRS} and \gls{PWS} the challenges of tiered polyglot software development are evidenced in real world studies. As recent examples, a study of GitHub open source projects found an average of five different languages in each project, with many using tiered architectures~\citep{mayer2017multi}. An earlier empirical study of GitHub shows that using more languages to implement a project has a significant effect on project quality, since it increases defects~\citep{kochhar2016large}. A study of \gls{IOT} stack developers found that interoperation poses a real challenge, that microservices blur the abstraction between tiers, and that both testing and scaling \gls{IOT} applications to more devices are hard~\citep{motta2018challenges}. One way of minimising the challenges of developing tiered polyglot \gls{IOT} software is to standardise and reuse components. This approach has been hugely successful for web stacks, e.g.\ browser standards. The W3C -Web of Things aims to facilitate re-use by providing standardised metadata and other re-usable technological \gls{IOT} building blocks~\citep{guinard_building_2016}. However, the Web of Things has yet to gain widespread adoption. Moreover, as it is based on web technology, it requires the \emph{thing} to run a web server, significantly increasing the hardware requirements. +Web of Things aims to facilitate re-use by providing standardised metadata and other re-usable technological \gls{IOT} building blocks~\citep{guinard_building_2016}. However, the Web of Things has yet to gain widespread adoption. Moreover, as it is based on web technology, it requires the \emph{thing} to run a web server, significantly increasing the hardware requirements. \section{Tierless languages}% \label{sec_t4t:TiredvsTierless} -A radical approach to overcoming the challenges raised by tiered distributed software is to use a tierless programming language that eliminates the semantic friction between tiers by generating code for all tiers, and all communication between tiers, from a single program. +A radical approach to overcoming the challenges raised by tiered distributed software is to use a tierless programming language that eliminates the semantic friction between tiers by generating code for all tiers, and all communication between tiers, from a single program. %\adriancomment{Also referred to as multi-tier programming, tierless language applications usually utilise a single language, paradigm and type system, and the entire system is simultaneously checked by the compiler~\citep{weisenburger2020survey}.} Typically a tierless program uses a single language, paradigm and type system, and the entire distributed system is simultaneously checked by the compiler. @@ -178,18 +170,18 @@ There are established tierless languages for web development, both standalone la Example standalone tierless web languages are Links~\citep{cooper2006links} and Hop~\citep{serrano2006hop}. From a single declarative program the client, server and database code is simultaneously checked by the compiler, and compiled to the required component languages. For example, Links compiles to HTML and JavaScript for the client side and to SQL on the server-side to interact with the database system. -An example tierless web framework that uses a \gls{DSL} is Haste~\citep{10.1145/2775050.2633367}, that embeds the \gls{DSL} in \gls{HASKELL}. Haste programs are compiled multiple times: the server code is generated by the standard \gls{GHC} \gls{HASKELL} compiler~\citep{hall1993glasgow}; Javascript for the client is generated by a custom \gls{GHC} compiler backend. The design leverages \gls{HASKELL}'s high-level programming abstractions and strong typing, and benefits from \gls{GHC}: a mature and sophisticated compiler. +An example tierless web framework that uses a \gls{DSL} is Haste~\citep{10.1145/2775050.2633367}, that embeds the \gls{DSL} in \gls{HASKELL}. Haste programs are compiled multiple times: the server code is generated by the standard \gls{GHC} \gls{HASKELL} compiler~\citep{hall1993glasgow}; Javascript for the client is generated by a custom \gls{GHC} compiler backend. The design leverages \gls{HASKELL}'s high-level programming abstractions and strong typing, and benefits from \gls{GHC}: a mature and sophisticated compiler. \subsection{Tierless \texorpdfstring{\gls{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 microprocessors. 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. -\subsubsection{\texorpdfstring{\Glspl{DSL}}{DSLs} for microcontrollers} -Many \glspl{DSL} provide high-level programming for microcontrollers, for example providing strong typing and memory safety. +\subsubsection{\texorpdfstring{\Glspl{DSL}}{DSLs} for microprocessors} +Many \glspl{DSL} provide high-level programming for microprocessors, 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 \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. \subsubsection{\texorpdfstring{\Gls{FRP}}{Functional reactive programming}} @@ -204,7 +196,7 @@ TOP allows for more complex collaboration patterns than \gls{FRP}~\citep{wang_ma \subsubsection{Erlang/Elixir \texorpdfstring{\gls{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 \gls{C}\slash\gls{CPP}) on a resource-constrained microprocessor 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{\gls{IOT}}{IoT} languages}% @@ -215,26 +207,26 @@ This study compares a pair of tierless \gls{IOT} languages with conventional tie \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}. +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}. +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/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. \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}. -\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}. +\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}. %\mlcomment{I think this is fine} \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. +In many \gls{IOT} systems the sensor nodes are microprocessors 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. +Techniques such as over-the-air programming and interpreters allow microprocessors to be dynamically provisioned, increasing their maintainability and resilience. For example Baccelli et al.\ provide a single language \gls{IOT} system based on the RIOT \gls{OS} that allows runtime deployment of code snippets called containers~\citep{baccelli_reprogramming_2018}. 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}. @@ -251,15 +243,15 @@ Placement happens automatically as part of the first-class splitting constructs \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}. +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 microprocessors 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/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}. 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{boer_de_secure_2020}. +\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{boer_de_secure_2020}. \section{Task-oriented and \texorpdfstring{\gls{IOT}}{IoT} programming in \texorpdfstring{\gls{CLEAN}}{Clean}} @@ -268,11 +260,11 @@ To make this paper self-contained we provide a concise overview of \gls{CLEAN}, \Gls{CLEAN} is a statically typed functional programming language similar to \gls{HASKELL}: both languages are pure and non-strict~\citep{achten_clean_2007}. A key difference is how state is handled: \gls{HASKELL} typically embeds stateful actions in the \haskellinline{IO} Monad~\citep{peyton_jones_imperative_1993,wiki:IO}. In contrast, \gls{CLEAN} has a uniqueness type system to ensure the single-threaded use of stateful objects like files and windows~\citep{barendsen_smetsers_1996}. -Both \gls{CLEAN} and \gls{HASKELL} support fairly similar models of generic programming~\citep{ComparingGenericProgramming}, enabling functions to work on many types. As we shall see generic programming is heavily used in task-oriented programming~\citep{GenericProgrammingExtensionForClean,HinzeGenericFunctionalProgramming}, for example to construct web editors and communication protocols that work for any user-defined datatype. +Both \gls{CLEAN} and \gls{HASKELL} support fairly similar models of generic programming~\citep{ComparingGenericProgramming}, enabling functions to work on many types. As we shall see generic programming is heavily used in task-oriented programming~\citep{GenericProgrammingExtensionForClean,HinzeGenericFunctionalProgramming}, for example to construct web editors and communication protocols that work for any user-defined datatype. \subsection{\texorpdfstring{\Acrlong{TOP}}{Task-oriented programming}} -\Gls{TOP} is a declarative programming paradigm for constructing interactive distributed systems~\citep{plasmeijer_task-oriented_2012}. +\Gls{TOP} is a declarative programming paradigm for constructing interactive distributed systems~\citep{plasmeijer_task-oriented_2012}. Tasks are the basic blocks of \gls{TOP} and represent work that needs to be done in the broadest sense. Examples of typical tasks range from allowing a user to complete a form, controlling peripherals, moderating other tasks, or monitoring a database. From a single declarative description of tasks all of the required software components are generated. @@ -281,13 +273,13 @@ That is, from a single \gls{TOP} program the language implementation automatical Application areas range from simple web forms or blinking \glspl{LED} to multi-user distributed collaboration between people and machines~\citep{oortgiese_distributed_2017}. -\Gls{TOP} adds three concepts: tasks, task combinators, and \glspl{SDS}. Example basic tasks are web editors for user-defined datatypes, reading some \gls{IOT} sensor, or controlling peripherals like a servo motor. +\Gls{TOP} adds three concepts: tasks, task combinators, and \glspl{SDS}. Example basic tasks are web editors for user-defined datatypes, reading some \gls{IOT} sensor, or controlling peripherals like a servo motor. Task combinators compose tasks into more advanced tasks, either in parallel or sequential and allow task values to be observed by other tasks. As tasks can be returned as the result of a function, recursion can be freely used, e.g.\ to express the repetition of tasks. There are also standard combinators for common patterns. Tasks can exchange information via \glspl{SDS}~\citep{ParametricLenses}. All tasks involved can atomically observe and change the value of a typed \gls{SDS}, allowing more flexible communication than with task combinators. -\glspl{SDS} offer a general abstraction of data shared by different tasks, analogous to variables, persistent values, files, databases and peripherals like sensors. Combinators compose \glspl{SDS} into a larger \gls{SDS}, and +\glspl{SDS} offer a general abstraction of data shared by different tasks, analogous to variables, persistent values, files, databases and peripherals like sensors. Combinators compose \glspl{SDS} into a larger \gls{SDS}, and parametric lenses define a specific view on an \gls{SDS}. @@ -296,11 +288,11 @@ parametric lenses define a specific view on an \gls{SDS}. \label{sec_t4t:itasks} -The \gls{ITASK} \gls{EDSL} is designed for constructing multi-user distributed applications, including web~\citep{TOP-ICFP07} or \gls{IOT} applications. +The \gls{ITASK} \gls{EDSL} is designed for constructing multi-user distributed applications, including web~\citep{TOP-ICFP07} or \gls{IOT} applications. Here we present \gls{ITASK} by example, and the first is a complete program to repeatedly read the room temperature from a digital humidity and temperature (DHT) sensor attached to the machine and display it on a web page (\cref{lst_t4t:itaskTemp}). The first line is the module name, the third imports the \cleaninline{iTask} module, and the main function (\cref{lst_t4t:itaskTemp:systemfro,lst_t4t:itaskTemp:systemto}) launches \cleaninline{readTempTask} and the \gls{ITASK} system to generate the web interface in \cref{fig_t4t:itaskTempSimple}. -Interaction with a device like the DHT sensor using a protocol like 1-Wire or gls{I2C} is abstracted into a library. So the \cleaninline{readTempTask} task starts by creating a \cleaninline{dht} sensor object (\cref{lst_t4t:itaskTemp:dhtDef}) thereafter \cleaninline{repeatEvery} executes a task at the specified \cleaninline{interval}. This task reads the temperature from the \cleaninline{dht} sensor, and with a sequential composition combinator \cleaninline{>>~} passes the \cleaninline{temp} value to \cleaninline{viewInformation} that displays it on the web page (\cref{lst_t4t:itaskTemp:viewInformation}). +Interaction with a device like the DHT sensor using a protocol like 1-Wire or gls{I2C} is abstracted into a library. So the \cleaninline{readTempTask} task starts by creating a \cleaninline{dht} sensor object (\cref{lst_t4t:itaskTemp:dhtDef}) thereafter \cleaninline{repeatEvery} executes a task at the specified \cleaninline{interval}. This task reads the temperature from the \cleaninline{dht} sensor, and with a sequential composition combinator \cleaninline{>>~} passes the \cleaninline{temp} value to \cleaninline{viewInformation} that displays it on the web page (\cref{lst_t4t:itaskTemp:viewInformation}). The tuning combinator \cleaninline{<<@} adds a label to the web editor displaying the temperature. Crucially, the \gls{ITASK} implementation transparently ships parts of the code for the web-interface to be executed in the browser, and \cref{fig_t4t:itaskTempSimple} shows the UML deployment diagram. \begin{lstClean}[% @@ -316,12 +308,12 @@ Start world = doTasks readTempTask world [+\label{lst_t4t:itaskTemp:systemto}+] readTempTask :: Task Real readTempTask = - withDHT IIO_TempID \dht -> [+\label{lst_t4t:itaskTemp:dhtDef}+] - repeatEvery interval ( [+\label{lst_t4t:itaskTemp:repeat}+] - temperature dht >>~ \temp -> [+\label{lst_t4t:itaskTemp:readDHT}+] - viewInformation [] temp <<@ [+\label{lst_t4t:itaskTemp:viewInformation}+] - Label "Temperature" [+\label{lst_t4t:itaskTemp:label}+] - ) + withDHT IIO_TempID \dht -> [+\label{lst_t4t:itaskTemp:dhtDef}+] + repeatEvery interval ( [+\label{lst_t4t:itaskTemp:repeat}+] + temperature dht >>~ \temp -> [+\label{lst_t4t:itaskTemp:readDHT}+] + viewInformation [] temp <<@ [+\label{lst_t4t:itaskTemp:viewInformation}+] + Label "Temperature" [+\label{lst_t4t:itaskTemp:label}+] + ) \end{lstClean} \begin{figure}[ht] @@ -340,7 +332,7 @@ readTempTask = \label{fig_t4t:itaskTempSimple} \end{figure} -SimpleTempSensor only reports instantaneous temperature measurements. Extending it to store and manipulate timed temperature records produces a tiny tierless web application: TempHistory in \cref{lst_t4t:TempHistory}. A tierless \gls{IOT} system can be controlled from a web interface in exactly the same way, e.g.\ to view and set the measurement frequencies of each of the room sensors. \Cref{lst_t4t:itaskTemp:Measurement} defines a record to store \cleaninline{time} and \cleaninline{temp} measurements. Task manipulations are derived for \cleaninline{Measurement} (\cref{lst_t4t:itaskTemp:derive}) and these include displaying measurements in a web-editor and storing them in a file. +SimpleTempSensor only reports instantaneous temperature measurements. Extending it to store and manipulate timed temperature records produces a tiny tierless web application: TempHistory in \cref{lst_t4t:TempHistory}. A tierless \gls{IOT} system can be controlled from a web interface in exactly the same way, e.g.\ to view and set the measurement frequencies of each of the room sensors. \Cref{lst_t4t:itaskTemp:Measurement} defines a record to store \cleaninline{time} and \cleaninline{temp} measurements. Task manipulations are derived for \cleaninline{Measurement} (\cref{lst_t4t:itaskTemp:derive}) and these include displaying measurements in a web-editor and storing them in a file. \Cref{lst_t4t:itaskTemp:measurementsSDS} defines a persistent \gls{SDS} to store a list of measurements, and for communication between tasks. The \gls{SDS} is analogous to the SQL DBMS in many tiered web applications. TempHistory defines two tasks that interact with the \texttt{mea\-sure\-ments\-SDS}: \texttt{mea\-sure\-Task} adds measurements at each detected change in the temperature. @@ -354,7 +346,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}, - label={lst_t4t:TempHistory}] + label={lst_t4t:TempHistory}] module TempHistory import iTasks, iTasks.Extensions.DateTime @@ -367,7 +359,7 @@ measurementsSDS = sharedStore "measurements" [] measureTask :: Task () [+\label{lst_t4t:itaskTemp:measureTask}+] measureTask = - withDHT IIO_TempID \dht -> + withDHT IIO_TempID \dht -> let task old = temperature dht >>* [+\label{lst_t4t:itaskTemp:step}+] [ OnValue (ifValue ((<>) old) \temp -> [+\label{lst_t4t:itaskTemp:action1}+] @@ -401,8 +393,8 @@ mainTask = controlSDS False -|| measureTask The \cleaninline{controlSDS} task illustrates communication from the web page user and persistent data manipulation. That is, it generates a web page that allows users to control their view of the temperature measurements, as illustrated in \cref{fig_t4t:TempHistory}. The page contains \begin{enumerate*} \item a web editor to enter the number \cleaninline{n} of elements to display, generated on \cref{lst_t4t:itaskTemp:enter}. - \item A display of the \cleaninline{n} most recent temperature and time measurements, \crefrange{lst_t4t:itaskTemp:view}{lst_t4t:itaskTemp:viewend}. - \item Three buttons that are again combined with the step combinator \cleaninline{>>*}, \crefrange{lst_t4t:itaskTemp:viewend}{lst_t4t:itaskTemp:actionend}. The \cleaninline{Clear} button is \cleaninline{always} enabled and sets the \gls{SDS} to an empty list before calling \cleaninline{controlSDS} recursively. The \cleaninline{Take} button is only enabled when the web editor produces a positive \cleaninline{n} and updates the \cleaninline{measurementsSDS} with the \cleaninline{n} most recent measurements before calling \cleaninline{controlSDS} recursively. The final action is \cleaninline{always} enabled and calls \cleaninline{controlSDS} recursively with the negation of the \cleaninline{byTemp} argument to change the sorting criteria. + \item A display of the \cleaninline{n} most recent temperature and time measurements, \crefrange{lst_t4t:itaskTemp:view}{lst_t4t:itaskTemp:viewend}. + \item Three buttons that are again combined with the step combinator \cleaninline{>>*}, \crefrange{lst_t4t:itaskTemp:viewend}{lst_t4t:itaskTemp:actionend}. The \cleaninline{Clear} button is \cleaninline{always} enabled and sets the \gls{SDS} to an empty list before calling \cleaninline{controlSDS} recursively. The \cleaninline{Take} button is only enabled when the web editor produces a positive \cleaninline{n} and updates the \cleaninline{measurementsSDS} with the \cleaninline{n} most recent measurements before calling \cleaninline{controlSDS} recursively. The final action is \cleaninline{always} enabled and calls \cleaninline{controlSDS} recursively with the negation of the \cleaninline{byTemp} argument to change the sorting criteria. \end{enumerate*} \begin{figure}[ht] @@ -418,7 +410,7 @@ The \cleaninline{controlSDS} task illustrates communication from the web page us \caption{Web page sorted by temperature.} \end{subfigure} \caption{Web pages generated by the \cleaninline{TempHistory} \gls{CLEAN}\slash\gls{ITASK} tierless web application. - The \cleaninline{Take} button is only enabled when the topmost editor contains a positive number. + The \cleaninline{Take} button is only enabled when the topmost editor contains a positive number. }% \label{fig_t4t:TempHistory} \end{figure} @@ -435,10 +427,10 @@ The \cleaninline{controlSDS} task illustrates communication from the web page us \subsection{Engineering tierless \texorpdfstring{\gls{IOT}}{IoT} systems with \texorpdfstring{\gls{ITASK}}{iTask}}% \label{sec_t4t:itaskIOT} -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. +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. -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) an \gls{SDS}: \cleaninline{dateTimeStampedShare} \cleaninline{latestTemp}. +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) an \gls{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}). The \cleaninline{tempSDS} is very similar to the \cleaninline{measurementsSDS} from the previous listings. @@ -451,9 +443,9 @@ The result of reading is the head of the list, if it exists. The type for writing \cleaninline{latestTemp} is a tuple with a new \cleaninline{DateTime} and temperature as \cleaninline{Real}. \begin{lstClean}[% - numbers=left, + 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.}, - label={lst_t4t:itaskTempFull}] + label={lst_t4t:itaskTempFull}] tempSDS :: SimpleSDSLens [(DateTime, Real)] tempSDS = sharedStore "temperatures" [] @@ -489,32 +481,32 @@ mainTask [+\label{lst_t4t:itaskTempFull:main}+] \caption{Deployment diagram.} \end{subfigure} \caption{Tierless \gls{ITASK} \gls{CRTS} temperature sensing \gls{IOT} system.}% - \label{fig_t4t:cwtsweb} + \label{fig_t4t:cwtsweb} \end{figure} \subsection{The \texorpdfstring{\gls{MTASK}}{mTask} \texorpdfstring{\gls{EDSL}}{eDSL}}% \label{sec_t4t:mtasks} -In many \gls{IOT} systems the sensor nodes are resource constrained, e.g.\ inexpensive microcontrollers. These are far cheaper, and consume far less power, than a single-board computer like a Raspberry Pi. Microcontrollers also allow the programmer to easily control peripherals like sensors and actuators via the \gls{IO} pins of the processor. +In many \gls{IOT} systems the sensor nodes are resource constrained, e.g.\ inexpensive microprocessors. These are far cheaper, and consume far less power, than a single-board computer like a Raspberry Pi. Microprocessors also allow the programmer to easily control peripherals like sensors and actuators via the \gls{IO} pins of the processor. -Microcontrollers have limited memory capacity, compute power and communication bandwidth, and hence typically no \gls{OS}. +Microprocessors have limited memory capacity, compute power and communication bandwidth, and hence typically no \gls{OS}. These limitations make it impossible to run an \gls{ITASK} server: -there is no \gls{OS} to start the remote task, the code of the task is too big to fit in the available memory and the microcontroller processor is too slow to run it. -The \gls{MTASK} \gls{EDSL} is designed to bridge this gap: \gls{MTASK} tasks can be communicated from the server to the sensor node, to execute within the limitations of a typical microcontroller, while providing programming abstractions that are consistent with \gls{ITASK}. +there is no \gls{OS} to start the remote task, the code of the task is too big to fit in the available memory and the microprocessor processor is too slow to run it. +The \gls{MTASK} \gls{EDSL} is designed to bridge this gap: \gls{MTASK} tasks can be communicated from the server to the sensor node, to execute within the limitations of a typical microprocessor, while providing programming abstractions that are consistent with \gls{ITASK}. Like \gls{ITASK}, \gls{MTASK} is task oriented, e.g.\ there are primitive tasks that produce intermediate values, a restricted set of task combinators to compose the tasks, and (recursive) functions to construct tasks. \Gls{MTASK} tasks communicate using task values or \glspl{SDS} that may be local or remote, and may be shared by some \gls{ITASK} 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. +Apart from the \gls{EDSL}, the \gls{MTASK} system contains a featherlight domain-specific \emph{operating system} running on the microprocessor. +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 microprocessor. +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 microprocessor before exhausting the flash memory. \subsection{Engineering tierless \texorpdfstring{\gls{IOT}}{IoT} systems with \texorpdfstring{\gls{MTASK}}{mTask}}% \label{sec_t4t:mtaskIOT} -A tierless \gls{CLEAN} \gls{IOT} system with microcontroller sensor nodes integrates a set of \gls{ITASK} tasks that specify the application and presentation layers with a set of \gls{MTASK}s that specify the perception and network layers. -We illustrate with \gls{CWTS}: a simple room temperature sensor with a web display. \gls{CWTS} is equivalent to the \gls{ITASK} \gls{CRTS} (\cref{lst_t4t:itaskTempFull}), except that the sensor node is a Wemos microcontroller. +A tierless \gls{CLEAN} \gls{IOT} system with microprocessor sensor nodes integrates a set of \gls{ITASK} tasks that specify the application and presentation layers with a set of \gls{MTASK}s that specify the perception and network layers. +We illustrate with \gls{CWTS}: a simple room temperature sensor with a web display. \gls{CWTS} is equivalent to the \gls{ITASK} \gls{CRTS} (\cref{lst_t4t:itaskTempFull}), except that the sensor node is a Wemos microprocessor. \Cref{lst_t4t:mtaskTemp} shows the complete program, and the key function is \cleaninline{devTask} with a top-level \cleaninline{Main} type (\cref{lst_t4t:mtaskTemp:devTask}). In \gls{MTASK} functions, shares, and devices can only be defined at this top level. The program uses the same shares \cleaninline{tempSDS} and~\cleaninline{latestTemp} as \gls{CRTS}, and for completeness we repeat those definitions. @@ -527,14 +519,14 @@ This new \gls{SDS} of type \cleaninline{Real} is lifted to the \gls{MTASK} progr %The \cleaninline{mapRead} ensures that every write to \cleaninline{localSds} is automatically time-stamped when it is written to \cleaninline{latestTemp}. -The \cleaninline{mainTask} is a simple \gls{ITASK} task that starts the \cleaninline{devTask} \gls{MTASK} task on the device identified by \cleaninline{deviceInfo} (\cref{lst_t4t:mtaskTemp:withdevice}). At runtime the \gls{MTASK} slice is compiled to byte code, shipped to the indicated device, and launched. Thereafter, \cleaninline{mainTask} reads temperature values from the \cleaninline{latestTemp} \gls{SDS} that is shared with the \gls{MTASK} device, and displays them on a web page (\cref{fig_t4t:cwtsweb}). The \gls{SDS}---shared with the device using \cleaninline{liftsds}---automatically communicates new temperature values from the microcontroller to the server. +The \cleaninline{mainTask} is a simple \gls{ITASK} task that starts the \cleaninline{devTask} \gls{MTASK} task on the device identified by \cleaninline{deviceInfo} (\cref{lst_t4t:mtaskTemp:withdevice}). At runtime the \gls{MTASK} slice is compiled to byte code, shipped to the indicated device, and launched. Thereafter, \cleaninline{mainTask} reads temperature values from the \cleaninline{latestTemp} \gls{SDS} that is shared with the \gls{MTASK} device, and displays them on a web page (\cref{fig_t4t:cwtsweb}). The \gls{SDS}---shared with the device using \cleaninline{liftsds}---automatically communicates new temperature values from the microprocessor to the server. While this simple application makes limited use of the \gls{MTASK} \gls{EDSL}, it illustrates some powerful \gls{MTASK} program abstractions like basic tasks, task combinators, named recursive and parameterized tasks, and \glspl{SDS}. Function composition (\cref{lst_t4t:mtaskTemp:o}) and currying (\cref{lst_t4t:mtaskTemp:setSds}) are inherited from the \gls{CLEAN} host language. As \gls{MTASK} tasks are dynamically compiled, it is also possible to select and customise tasks as required at runtime. For example, the interval used in the \cleaninline{rpeatevery} task (\cref{lst_t4t:mtaskTemp:rpeatevery}) could be a parameter to the \cleaninline{devTask} function. -\newcommand{\srcmark}[1]{\marginpar[\small\emph{#1}]{\small\emph{#1}}} +\newcommand{\srcmark}[1]{\marginpar[\footnotesize\emph{#1}]{\footnotesize\emph{#1}}} \begin{lstClean}[% numbers=left, caption={ @@ -571,7 +563,7 @@ mainTask :: Task Real mainTask = withDevice deviceInfo \dev -> liftmTask} devTask dev[+\label{lst_t4t:mtaskTemp:liftmtask}+][+\label{lst_t4t:mtaskTemp:withdevice}+][+\label{lst_t4t:mtaskTemp:co2}\srcmark{CO}+] -|| viewSharedInformation [] latestTemp[+\label{lst_t4t:mtaskTemp:wi1}\srcmark{WI}+] - <<@ Title "Latest temperature"[+\label{lst_t4t:mtaskTemp:wi2}+] // WI + <<@ Title "Latest temperature"[+\label{lst_t4t:mtaskTemp:wi2}\srcmark{WI}+] \end{lstClean} % @@ -588,18 +580,18 @@ mainTask \caption{Deployment diagram.} \end{subfigure} \caption{Tierless \gls{ITASK}\slash\gls{MTASK} \gls{CWTS} temperature sensing \gls{IOT} system.}% - \label{fig_t4t:cwtsDiagram} + \label{fig_t4t:cwtsDiagram} \end{figure} \section{\texorpdfstring{\gls{UOG}}{UoG} smart campus case study}% \label{sec_t4t:Case} The basis for our comparison between tiered and tierless technologies are four \gls{IOT} systems that all conform to the \gls{UOG} smart campus specifications (\cref{sec_t4t:OperationalComparison}). There is a small (12 room) deployment of the conventional \gls{PYTHON}-based \gls{PRS} stack that uses Raspberry Pi supersensors, and its direct comparator is the tierless \gls{CRS} implementation: also deployed on Raspberry Pis. -To represent the more common microcontroller sensor nodes we select ESP8266X powered Wemos D1 Mini microcontrollers. To evaluate tierless technologies on microcontrollers we compare the conventional \gls{PYTHON}\slash\gls{MICROPYTHON} \gls{PWS} stack with the tierless \gls{CWS} implementation. +To represent the more common microprocessor sensor nodes we select ESP8266X powered Wemos D1 Mini microcontrollers. To evaluate tierless technologies on microcontrollers we compare the conventional \gls{PYTHON}\slash\gls{MICROPYTHON} \gls{PWS} stack with the tierless \gls{CWS} implementation. %The four systems under comparison are different in architecture ---tiered or tierless--- and type of sensor node hardware ---regular or embedded--- but they share many properties as well. -%The embedded sensor nodes are all implemented on the ESP8266X powered Wemos D1 Mini microcontroller. -A similar range of commodity sensors is connected to both the Raspberry Pi and Wemos sensor nodes using various low-level communication protocols such as \gls{GPIO}, \gls{I2C}, \gls{SPI} and \gls{ONEWIRE}. The sensors are as follows: Temperature \& Humidity: LOLIN SHT30; Light: LOLIN BH1750; Motion: LOLIN \gls{PIR}; Sound: SparkFun SEN-12642; \gls{ECO2}: SparkFun CCS811. +%The embedded sensor nodes are all implemented on the ESP8266X powered Wemos D1 Mini microprocessor. +A similar range of commodity sensors is connected to both the Raspberry Pi and Wemos sensor nodes using various low-level communication protocols such as \gls{GPIO}, \gls{I2C}, \gls{SPI} and \gls{ONEWIRE}. The sensors are as follows: Temperature \& Humidity: LOLIN SHT30; Light: LOLIN BH1750; Motion: LOLIN \gls{PIR}; Sound: SparkFun SEN-12642; \gls{ECO2}: SparkFun CCS811. \Cref{fig_t4t:wemos_prss} shows both a prototype Wemos-based sensor node and sensors and a Raspberry Pi supersensor. Three different development teams developed the four implementations: \gls{CWS} and \gls{CRS} were engineered by a single developer. @@ -628,13 +620,13 @@ Communication between a sensor node and the server is always initiated by the no relatively powerful Raspberry Pi 3 Model Bs. There is a simple object-oriented \gls{PYTHON} collector for configuring the sensors and reading their values. The collector daemon service marshals the sensor data and transmits using \gls{MQTT} to the central monitoring server at a preset frequency. 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}. +In contrast to \gls{PRS}, \gls{PWS}'s sensor nodes are microprocessors 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. 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. +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. \subsection{Tierless implementations} @@ -646,7 +638,7 @@ 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. 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 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}. +\Gls{CWS}'s sensor nodes are Wemos microprocessors 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}. \begin{figure}[ht] \centering @@ -664,13 +656,13 @@ Communication from the sensor node to the server is implicit and happens via \gl \label{fig_t4t:webinterfaces} \end{figure} -\subsection{Operational Equivalence}% +\subsection{Operational equivalence}% \label{sec_t4t:OperationalComparison} To ensure that the comparison reported in the following sections is based on \gls{IOT} stacks with equivalent functionality, we demonstrate that \gls{PWS}, \gls{CWS} and \gls{CRS}, like \gls{PRS}, meet the functional requirements for the \gls{UOG} smart campus sensor system. We also compare the sensor node power consumption and memory footprint. -\subsubsection{Functional Requirements}% +\subsubsection{Functional requirements}% \label{sec_t4t:Validation} The main goal of the \gls{UOG} smart campus project is to provide a testbed for sensor nodes and potentially other devices to act as a data collection and computation platform for the \gls{UOG} smart campus. The high-level functional requirements, as specified by the \gls{UOG} smart campus project board, are as follows. The system should: @@ -695,7 +687,7 @@ The main goal of the \gls{UOG} smart campus project is to provide a testbed for \end{enumerate} All four smart campus implementations meet these high-level requirements. -\subsubsection{Functional Equivalence} +\subsubsection{Functional equivalence} Observation of the four implementations shows that they operate as expected, e.g.\ detecting light or motion. To illustrate \cref{fig_t4t:webinterfaces} shows the web interface for the implementations where \gls{CWS} and \gls{CRS} are deployed in a different room from \gls{PWS} and \gls{PRS}. @@ -704,11 +696,11 @@ All four implementations use an identical set of inexpensive sensors, so we expe % Phil: lets see what the reviewers say -\subsubsection{Memory and Power Consumption}% +\subsubsection{Memory and power consumption}% %\subsubsection{Memory Consumption}% \label{sec_t4t:MemPower} -\paragraph{Memory} By design sensor nodes are devices with limited computational capacity, and memory is a key restriction. Even supersensors often have less than a \unit{\gibi\byte} of memory, and microcontrollers often have just tens of \unit{\kibi\byte{}s}. +\paragraph{Memory} By design sensor nodes are devices with limited computational capacity, and memory is a key restriction. Even supersensors often have less than a \unit{\gibi\byte} of memory, and microprocessors often have just tens of \unit{\kibi\byte{}s}. %In a tiered implementation the memory residency of the sensor node code can be minimised by carefully crafting it in a language that minimises memory residency. However, ... As the tierless languages synthesize the code to be executed on the sensor nodes, we need to confirm that the generated code is sufficiently memory efficient. @@ -720,15 +712,15 @@ As the tierless languages synthesize the code to be executed on the sensor nodes \toprule \multicolumn{1}{c}{\gls{PWS}} & \multicolumn{1}{c}{\gls{PRS}} & \multicolumn{1}{c}{\gls{CWS}} & \multicolumn{1}{c}{\gls{CRS}}\\ \midrule - \num{20270} & \num{3557806} & \num{880} & \num{2726680}\\ + \num{20270} & \num{3557806} & \num{880} & \num{2726680}\\ \bottomrule \end{tabular} \end{table} -\Cref{tbl_t4t:mem} shows the maximum memory residency after garbage collection of the sensor node for all four smart campus implementations. The smart campus sensor node programs executing on the Wemos microcontrollers have low maximum residencies: \qty{20270}{\byte} for \gls{PWS} and \qty{880}{\byte} for \gls{CWS}. In \gls{CWS} the \gls{MTASK} system generates very high level \gls{TOP} byte code that is interpreted by the \gls{MTASK} virtual machine and uses a small and predictable amount of heap memory. +\Cref{tbl_t4t:mem} shows the maximum memory residency after garbage collection of the sensor node for all four smart campus implementations. The smart campus sensor node programs executing on the Wemos microprocessors have low maximum residencies: \qty{20270}{\byte} for \gls{PWS} and \qty{880}{\byte} for \gls{CWS}. In \gls{CWS} the \gls{MTASK} system generates very high level \gls{TOP} byte code that is interpreted by the \gls{MTASK} virtual machine and uses a small and predictable amount of heap memory. In \gls{PWS}, the hand-written \gls{MICROPYTHON} is compiled to byte code for execution on the virtual machine. Low residency is achieved with a fixed size heap and efficient memory management. For example both \gls{MICROPYTHON} and \gls{MTASK} use fixed size allocation units and mark\&sweep garbage collection to minimise memory usage at the cost of some execution time~\citep{plamauer2017evaluation}. -The smart campus sensor node programs executing on the Raspberry Pis have far higher maximum residencies than those executing on the microcontrollers: \qty{3.5}{\mebi\byte} for \gls{PRS} and \qty{2.7}{\mebi\byte} for \gls{CRS}. In \gls{CRS} the sensor node code is a set of \gls{ITASK} executing on a full-fledged \gls{ITASK} server running in distributed child mode and this consumes far more memory. +The smart campus sensor node programs executing on the Raspberry Pis have far higher maximum residencies than those executing on the microprocessors: \qty{3.5}{\mebi\byte} for \gls{PRS} and \qty{2.7}{\mebi\byte} for \gls{CRS}. In \gls{CRS} the sensor node code is a set of \gls{ITASK} executing on a full-fledged \gls{ITASK} server running in distributed child mode and this consumes far more memory. %The memory used is actually very similar to the memory usage of the server with a single client connected. 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}.%\mlcomment{reference \url{https://docs.micropython.org/en/latest/genrst/index.html} ? It contains an overview of supported features} @@ -740,28 +732,28 @@ In summary the sensor node code generated by both tierless languages, \gls{ITASK %\mlcomment{There only seems to be anecdotal evidence of this. See: \url{https://www.element14.com/community/people/neilk/blog/2019/02/14/investigating-the-power-consumption-of-a-wemos-d1-mini-esp8266}} %\pkcomment{We can state that our own measurements are consistent the experience of others.} The 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}. -The Raspberry Pi supersensor node used in \gls{CRS} and \gls{PRS} use more power as they have a general purpose ARM processor and run mainstream Linux. With all sensors enabled, they consume \qtyrange{1}{2}{\watt}, depending on ambient load. So a microcontroller sensor node consumes an order of magnitude less power than a supersensor node. +The Raspberry Pi supersensor node used in \gls{CRS} and \gls{PRS} use more power as they have a general purpose ARM processor and run mainstream Linux. With all sensors enabled, they consume \qtyrange{1}{2}{\watt}, depending on ambient load. So a microprocessor sensor node consumes an order of magnitude less power than a supersensor node. -\section{Is Tierless \texorpdfstring{\gls{IOT}}{IoT} Programming Easier than Tiered?}% +\section[Is tierless \texorpdfstring{\gls{IOT}}{IoT} programming easier than tiered?]{\hspace{-9pt}Is tierless \texorpdfstring{\gls{IOT}}{IoT} programming easier than tiered?}% \label{sec_t4t:ProgrammingComparison} -This section investigates whether tierless languages make \gls{IOT} programming \emph{easier} by comparing the \gls{UOG} smart campus implementations. The \gls{CRS} and \gls{CWS} implementations allow us to evaluate tierless languages for +This section investigates whether tierless languages make \gls{IOT} programming \emph{easier} by comparing the \gls{UOG} smart campus implementations. The \gls{CRS} and \gls{CWS} implementations allow us to evaluate tierless languages for resource-rich and for resource-constrained sensor nodes respectively. The \gls{PRS} and \gls{PWS} allow a like-for-like comparison with tiered \gls{PYTHON} implementations. %\subsection{Temperature Sensor Illustration} -\subsection{Comparing Tiered and Tierless Codebases}% +\subsection{Comparing tiered and tierless codebases}% \label{sec_t4t:codesize} %A comparison of the Temperature sensor in \gls{PYTHON} Micropyton, Itask \& \gls{MTASK}. -\paragraph{Code Size} is widely recognised as an approximate measure of the development and maintenance effort required for a software system~\citep{rosenberg1997some}. \gls{SLOC} is a common code size metric, and is especially useful for multi-paradigm systems like \gls{IOT} systems. It is based on the simple principle that the more \gls{SLOC}, the more developer effort and the increased likelihood of bugs~\citep{rosenberg1997some}. It is a simple measure, not dependent on some formula, and can be automatically computed~\citep{sheetz2009understanding}. +\paragraph{Code size} is widely recognised as an approximate measure of the development and maintenance effort required for a software system~\citep{rosenberg1997some}. \gls{SLOC} is a common code size metric, and is especially useful for multi-paradigm systems like \gls{IOT} systems. It is based on the simple principle that the more \gls{SLOC}, the more developer effort and the increased likelihood of bugs~\citep{rosenberg1997some}. It is a simple measure, not dependent on some formula, and can be automatically computed~\citep{sheetz2009understanding}. Of course \gls{SLOC} must be used carefully as it is easily influenced by programming style, language paradigm, and counting method~\citep{alpernaswonderful}. Here we are counting code to compare development effort, use the same idiomatic programming style in each component, and only count lines of code, omitting comments and blank lines. -\Cref{table_t4t:multi} enumerates the \gls{SLOC} required to implement the \gls{UOG} smart campus functionalities in \gls{PWS}, \gls{PRS}, \gls{CWS} and \gls{CRS}. Both \gls{PYTHON} and \gls{CLEAN} implementations use the same server and communication code for Raspberry Pi and for Wemos sensor nodes (rows 5--7 of the table). +\Cref{table_t4t:multi} enumerates the \gls{SLOC} required to implement the \gls{UOG} smart campus functionalities in \gls{PWS}, \gls{PRS}, \gls{CWS} and \gls{CRS}. Both \gls{PYTHON} and \gls{CLEAN} implementations use the same server and communication code for Raspberry Pi and for Wemos sensor nodes (rows 5--7 of the table). The Sensor Interface (SI) refers to code facilitating the communication between the peripherals and the sensor node software. % formerly hardware interface Sensor Node (SN) code contains all other code on the sensor node that does not belong to any another category, such as control flow. % formerly device output %Server communication denotes code that provides the high level communication between the sensor node and the server. @@ -774,7 +766,7 @@ The most striking information in \cref{table_t4t:multi} is that \emph{the tierle \begin{table} \centering % used for centering table - \caption{Comparing tiered and tierless smart campus code sizes: \gls{SLOC} and number of source files. \gls{PWS} and \gls{CWS} execute on resource-constrained sensor nodes, while \gls{PRS} and \gls{CRS} execute on resource-rich sensor nodes.}% + \caption{Comparing tiered and tierless smart campus code sizes: \gls{SLOC} and number of source files. \gls{PWS} and \gls{CWS} execute on resource-constrained sensor nodes, while \gls{PRS} and \gls{CRS} execute on resource-rich sensor nodes.}% \label{table_t4t:multi} \begin{tabular}{l l c c c c } % centered columns (4 columns) \toprule @@ -796,7 +788,7 @@ The most striking information in \cref{table_t4t:multi} is that \emph{the tierle \end{tabular} \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 an \gls{SDS}, requiring just a few \gls{SLOC}. +\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 an \gls{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. @@ -807,22 +799,22 @@ The total size of \gls{CWS} and \gls{CRS} would be reduced by a factor of two an \label{fig_t4t:multipercentage} \end{figure} -\subsection{Comparing Codebases for Resource-Rich/Constrained Sensor Nodes}% +\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}. -The two tierless implementations are also similar in size: \gls{CWS} requiring 166 and \gls{CRS} 155 \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 microprocessors 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/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. -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}. +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}. -\subsection{Reduced Interoperation}% +\subsection{Reduced interoperation}% \label{sec_t4t:interoperation} \begin{table} \centering @@ -880,7 +872,7 @@ The tierless implementations use just two conceptually-similar \glspl{DSL} embed Interoperation \emph{increases the cognitive load on the developer} who must simultaneously think in multiple languages and paradigms. This is commonly known as semantic friction or impedance mismatch~\citep{ireland2009classification}. A simple illustration of this is that the tiered \gls{PRS} source code comprises some 38 source and configuration files, whereas the tierless \gls{CRS} requires just 3 files (\cref{table_t4t:multi}). The source could be structured as a single file, but to separate concerns is structured into three modules, one each for \glspl{SDS}, types, and control logic~\citep{wang_maintaining_2018}. -The developer must \emph{correctly interoperate the components}, e.g.\ adhere to the \gls{API} or communication protocols between components. The interoperation often entails additional programming tasks like marshalling or demarshalling data between components. For example, in the tiered \gls{PRS} and \gls{PWS} architectures, \gls{JSON} is used to serialise and deserialise data strings from the \gls{PYTHON} collector component before storing the data in the Redis database (\cref{lst_t4t:json}). +The developer must \emph{correctly interoperate the components}, e.g.\ adhere to the \gls{API} or communication protocols between components. The interoperation often entails additional programming tasks like marshalling or demarshalling data between components. For example, in the tiered \gls{PRS} and \gls{PWS} architectures, \gls{JSON} is used to serialise and deserialise data strings from the \gls{PYTHON} collector component before storing the data in the Redis database (\cref{lst_t4t:json}). %e.g.\ to marshall and demarshall data between components. %\mlcomment{A listing must have a caption for it to be labeled and referenced} @@ -902,7 +894,7 @@ try: To ensure correctness the developer \emph{must maintain type safety} across a range of very different languages and diverse type systems, and we explore this further in \cref{sec_t4t:typesafety}. The developer must also deal with the \emph{potentially diverse failure modes}, not only of each component, but also of their interoperation, e.g.\ if a value of an unexpected type is passed through an \gls{API}. We explore this further in \cref{sec_t4t:NetworkManagement}. -\subsection{Automatic Communication}% +\subsection{Automatic communication}% \label{sec_t4t:Communication} In conventional tiered \gls{IOT} implementations the developer must write and maintain code to communicate between tiers. For example \gls{PRS} and \gls{PWS} create, send and read \gls{MQTT}~\citep{light2017mosquitto} messages @@ -924,7 +916,7 @@ def main(): [+\ldots+] \end{lstPython} -In contrast, the tierless \gls{CWS} and \gls{CRS} communication is not only highly automated, but also automatically correct because matching sender and receiver code is generated by the compiler. \Cref{table_t4t:multi} shows that communication is specified in just 5 \gls{SLOC} in \gls{CWS} and 4 in \gls{CRS}, or just 3\% of the codebase (bottom bars in \cref{fig_t4t:multipercentage}). +In contrast, the tierless \gls{CWS} and \gls{CRS} communication is not only highly automated, but also automatically correct because matching sender and receiver code is generated by the compiler. \Cref{table_t4t:multi} shows that communication is specified in just 5 \gls{SLOC} in \gls{CWS} and 4 in \gls{CRS}, or just 3\% of the codebase (bottom bars in \cref{fig_t4t:multipercentage}). \Cref{lst_t4t:mtaskTemp} illustrates communication in a tierless \gls{IOT} language. That is, the \gls{CWTS} temperature sensor requires just three lines of communication code, and uses just three communication functions. The @@ -933,15 +925,15 @@ In contrast, the tierless \gls{CWS} and \gls{CRS} communication is not only high The \cleaninline{liftsds} on \cref{lst_t4t:mtaskTemp:liftsds} integrates \glspl{SDS} from \gls{ITASK} into \gls{MTASK}, allowing \gls{MTASK} tasks to interact with data from the \gls{ITASK} server. The exchange of data, user interface, and communication are all automatically generated. -\subsection{High Level Abstractions}% +\subsection{High level abstractions}% \label{sec_t4t:abstractions} Another reason that the tierless \gls{CLEAN} implementations are concise is because they use powerful higher order \gls{IOT} programming abstractions. For comprehensibility the simple temperature sensor from \cref{sec_t4t:mtasks} (\cref{lst_t4t:mtaskTemp}) is used to compare the expressive power of \gls{CLEAN} and \gls{PYTHON}-based \gls{IOT} programming abstractions. There are implementations for all four configurations: \gls{PRTS} (\gls{PYTHON} Raspberry Pi Temperature Sensor)\footnotemark, \gls{PWTS}\footnotemark[\value{footnote}]\todo{the dataset will be uploaded, the DOI is reserved.} -\footnotetext{Lubbers, M.; Koopman, P.; Ramsingh, A.; Singer, J.; Trinder, P. (2021): Source code, line counts and memory stats for PRS, PWS, PRT and PWT.\ Zenodo.\ \href{https://doi.org/10.5281/zenodo.5081386}{10.5281/zenodo.5081386}.}, \gls{CRTS}\footnotemark{} and \gls{CWTS}\footnotemark[\value{footnote}] \todo{the dataset will be uploaded, the DOI is reserved.} -\footnotetext{Lubbers, M.; Koopman, P.; Ramsingh, A.; Singer, J.; Trinder, P. (2021): Source code, line counts and memory stats for CRS, CWS, CRTS and CWTS.\ Zenodo.\ \href{https://doi.org/10.5281/zenodo.5040754}{10.5281/zenodo.5040754}.} -but as the programming abstractions are broadly similar, we compare only the \gls{PWTS} and \gls{CWTS} implementations. +\footnotetext{Lubbers, M.; Koopman, P.; Ramsingh, A.; Singer, J.; Trinder, P. (2021): Source code, line counts and memory stats for PRS, PWS, PRT and PWT.\ Zenodo.\ \href{https://doi.org/10.5281/zenodo.5081386}{10.5281/zenodo.5081386}.}, \gls{CRTS}\footnotemark{} and \gls{CWTS}\footnotemark[\value{footnote}] \todo{the dataset will be uploaded, the DOI is reserved.} +\footnotetext{Lubbers, M.; Koopman, P.; Ramsingh, A.; Singer, J.; Trinder, P. (2021): Source code, line counts and memory stats for CRS, CWS, CRTS and CWTS.\ Zenodo.\ \href{https://doi.org/10.5281/zenodo.5040754}{10.5281/zenodo.5040754}.} +but as the programming abstractions are broadly similar, we compare only the \gls{PWTS} and \gls{CWTS} implementations. Although the temperature sensor applications are small compared to the smart campus application, they share some typical \gls{IOT} stack traits. The architecture consists of a server and a single sensor node (\cref{fig_t4t:cwtsDiagram}). @@ -974,18 +966,18 @@ 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, functional programming 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, functional programming 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. 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}. -\section{Could Tierless \texorpdfstring{\gls{IOT}}{IoT} Programming Be More Reliable than Tiered?}% +\section{Could tierless \texorpdfstring{\gls{IOT}}{IoT} programming be more reliable than tiered?}% \label{sec_t4t:Discussion} This section investigates whether tierless languages make \gls{IOT} programming more reliable. Arguably the much smaller and simpler code base is inherently more understandable, and more likely to be correct. Here we explore specific language issues, namely those of preserving type safety, maintainability, failure management, and community support. -\subsection{Type Safety}% +\subsection{Type safety}% \label{sec_t4t:typesafety} Strong typing identifies errors early in the development cycle, and hence plays a crucial role in improving software quality. In consequence almost all modern languages provide strong typing, and encourage static typing to minimise runtime errors. % Phil: so widely known that a citation is unnecessary~\citep{madsen1990strong}. @@ -993,7 +985,7 @@ That said, many distributed system components written in languages that primaril In a typical tiered multi-language \gls{IOT} system the developer must integrate software in different languages with very different type systems, and potentially executing on different hardware. The challenges of maintaining type safety have long been recognised as a major component of the semantic friction in multi-language systems, e.g.\ \citep{ireland2009classification}. -Even if the different languages used in two components are both strongly typed, they may attribute, often quite subtly, different types to a value. Such type errors can lead to runtime errors, or the application silently reporting erroneous data. Such errors can be hard to find. Automatic detection of such errors is sometimes possible, but requires an addition tool like Jinn~\citep{Jinn,Furr2005}. +Even if the different languages used in two components are both strongly typed, they may attribute, often quite subtly, different types to a value. Such type errors can lead to runtime errors, or the application silently reporting erroneous data. Such errors can be hard to find. Automatic detection of such errors is sometimes possible, but requires an addition tool like Jinn~\citep{Jinn,Furr2005}. %Such errors can be hard to debug, partly because there is very limited tool support for detecting them %Phil: another possible source to discuss ~\citep{egyed1999automatically} @@ -1012,10 +1004,10 @@ channel = 'sensor_status.%s.%s' % (hostname, Analysis of the \gls{PRS} codebase reveals an instance where it, fairly innocuously, loses type safety. The fragment in \cref{lst_t4t:float} first shows a \pythoninline{double} sensor value being sent from the sensor node, and then shows the value being stored in Redis as a \pythoninline{string} on the server. As \gls{PWS} preserves the same server components it also suffers from the same loss of type safety. -\emph{A tierless language makes it possible to guarantee type safety across an entire \gls{IOT} stack}. For example the \gls{CLEAN} compiler guarantees static type safety as the entire \gls{CWS} software stack is type checked, and generated, from a single source. Tierless web stack languages like Links~\citep{cooper2006links} and Hop~\citep{serrano2006hop} provide the same guarantee for web stacks. +\emph{A tierless language makes it possible to guarantee type safety across an entire \gls{IOT} stack}. For example the \gls{CLEAN} compiler guarantees static type safety as the entire \gls{CWS} software stack is type checked, and generated, from a single source. Tierless web stack languages like Links~\citep{cooper2006links} and Hop~\citep{serrano2006hop} provide the same guarantee for web stacks. -\subsection{Failure Management}% +\subsection{Failure management}% \label{sec_t4t:NetworkManagement} Some \gls{IOT} applications, including smart campus and other building monitoring applications, require high sensor uptimes. Hence, if a sensor or sensor node fails the application layer must be notified, so that it can report the failure. In the \gls{UOG} smart campus system a building manager is alerted to replace the failed device. @@ -1031,7 +1023,7 @@ failover :: [TCPSettings] (Main (MTask BCInterpret a)) -> Task a failover [] _ = throw "Exhausted device pool" failover [d:ds] mtask = try ( withDevice d (liftmTask mtask) ) except where except MTEUnexpectedDisconnect = failover ds mtask - except _ = throw e + except _ = throw e \end{lstClean} In the \gls{UOG} smart campus application, this can be done by creating a pool of sensor nodes for each room and when a sensor node fails, assign another one to the task. @@ -1075,46 +1067,49 @@ There are few maintainers of the \glspl{DSL} and documentation is often sparse. Acquiring information about the systems requires distilling academic papers and referring to the source code. There is a \gls{CLEAN} \gls{IDE}, but it does not contain support for the \gls{ITASK} or \gls{MTASK} \glspl{DSL}. -\section{Comparing Tierless Languages for Resource-rich/constrained Sensor Nodes}% +\section{Comparing tierless languages for resource-rich/constrained sensor nodes}% \label{sec_t4t:ComparingTierless} -This section compares two tierless \gls{IOT} languages: one for resource-rich, and the other for resource-constrained, sensor nodes. Key issues are the extent to which the very significant resource constraints of a microcontroller limit the language, and the benefits of executing on bare metal, i.e.\ without an \gls{OS}. +This section compares two tierless \gls{IOT} languages: one for resource-rich, and the other for resource-constrained, sensor nodes. Key issues are the extent to which the very significant resource constraints of a microprocessor limit the language, and the benefits of executing on bare metal, i.e.\ without an \gls{OS}. With the tierless \gls{CLEAN} technologies described here, \gls{ITASK} are always used to program the application and presentation layers of the \gls{IOT} stack. So any differences occur in the perception and network layer programming. -If sensor nodes have the capacity to support \gls{ITASK}, a tierless \gls{IOT} system can be constructed in \gls{CLEAN} using only \gls{ITASK}, as in \gls{CRS}. Alternatively for sensor nodes with low computational power, like typical microcontrollers, \gls{MTASK} is used for the perception and network layers, as in \gls{CWS}. -This section compares the \gls{ITASK} and \gls{MTASK} \glspl{EDSL}, with reference to \gls{CRS} and \gls{CWS} as exemplars. \Cref{table_t4t:languagecomparison} summarises the differences between the \gls{CLEAN} embedded \gls{IOT} \glspl{EDSL} and their host language. +If sensor nodes have the capacity to support \gls{ITASK}, a tierless \gls{IOT} system can be constructed in \gls{CLEAN} using only \gls{ITASK}, as in \gls{CRS}. Alternatively for sensor nodes with low computational power, like typical microprocessors, \gls{MTASK} is used for the perception and network layers, as in \gls{CWS}. +This section compares the \gls{ITASK} and \gls{MTASK} \glspl{EDSL}, with reference to \gls{CRS} and \gls{CWS} as exemplars. \Cref{table_t4t:languagecomparison} summarises the differences between the \gls{CLEAN} embedded \gls{IOT} \glspl{EDSL} and their host language. \begin{table} \small \caption{Comparing tierless \gls{IOT} languages for resource-rich sensor nodes (\gls{ITASK} \gls{EDSL}), for resource-constrained sensor nodes (\gls{MTASK} \gls{EDSL}), and their \gls{CLEAN} host language.}% \label{table_t4t:languagecomparison} - \begin{tabularx}{\textwidth}{lXXX} + \begin{tabular}{llll} \toprule Property & \gls{CLEAN} & \gls{ITASK} & \gls{MTASK}\\ \midrule - Function for an \gls{IOT} System & Host Language & Specify distributed workflows & Specify sensor node workflow \\ - \midrule + Function for an \gls{IOT} System & Host Language & Specify distri- & Specify sensor\\ + & & buted workflows & node workflow \\ + \midrule Referentially transparent & Yes & Yes & Yes \\ - Evaluation strategy & Lazy & Lazy & Strict\\ + Evaluation strategy & Lazy & Lazy & Strict\\ Higher-order functions & Yes & Yes & No \\ User-defined datatypes & Yes & Yes & No \\ Task oriented & No & Yes & Yes \\ Higher-order tasks & {--} & Yes & No \\ - Execution Target & Commodity PC & Commodity PC and Browser & Microcontroller\\ - Language Implementation & Compiled or interpreted & Compiled and interpreted & Interpreted\\ + Execution Target & Commodity PC & Commodity PC & Microprocessor\\ + & & and Browser & Microprocessor\\ + Language Implementation & Compiled or & Compiled and & Interpreted\\ + & interpreted & interpreted & Interpreted\\ \bottomrule - \end{tabularx} + \end{tabular} \end{table} -\subsection{Language Restrictions for Resource-Constrained Execution} +\subsection{Language restrictions for resource-constrained execution} -Executing components on a resource-constrained sensor node imposes restrictions on programming abstractions available in a tierless \gls{IOT} language or \gls{DSL}. The small and fixed-size memory are key limitations. The limitations are shared by any high-level language that targets microcontrollers such as BIT, PICBIT, PICOBIT, Microscheme and uLisp~\citep{dube_bit:_2000,feeley_picbit:_2003,st-amour_picobit:_2009,suchocki_microscheme:_2015, johnson-davies_lisp_2020}. -Even in low level languages some language features are disabled by default when targeting microcontrollers, such as runtime type information (RTTI) in \gls{CPP}. +Executing components on a resource-constrained sensor node imposes restrictions on programming abstractions available in a tierless \gls{IOT} language or \gls{DSL}. The small and fixed-size memory are key limitations. The limitations are shared by any high-level language that targets microprocessors such as BIT, PICBIT, PICOBIT, Microscheme and uLisp~\citep{dube_bit:_2000,feeley_picbit:_2003,st-amour_picobit:_2009,suchocki_microscheme:_2015, johnson-davies_lisp_2020}. +Even in low level languages some language features are disabled by default when targeting microprocessors, such as runtime type information (RTTI) in \gls{CPP}. -Here we investigate the restrictions imposed by resource-constrained sensor nodes on \gls{MTASK}, in comparison with \gls{ITASK}. While \gls{ITASK} and \gls{MTASK} are by design superficially similar languages, to execute on resource-constrained sensor nodes \gls{MTASK} tasks are more restricted, and have a different semantics. +Here we investigate the restrictions imposed by resource-constrained sensor nodes on \gls{MTASK}, in comparison with \gls{ITASK}. While \gls{ITASK} and \gls{MTASK} are by design superficially similar languages, to execute on resource-constrained sensor nodes \gls{MTASK} tasks are more restricted, and have a different semantics. \Gls{MTASK} programs do not support user defined higher order functions, the only higher order functions available are the predefined \gls{MTASK} combinators. -Programmers can, however, use any construct of the \gls{CLEAN} host language to construct an \gls{MTASK} program, including higher order functions and arbitrary data types. For example folding an \gls{MTASK} combinator over a list of tasks. +Programmers can, however, use any construct of the \gls{CLEAN} host language to construct an \gls{MTASK} program, including higher order functions and arbitrary data types. For example folding an \gls{MTASK} combinator over a list of tasks. The only restriction is that any higher order function must be macro expanded to a first order \gls{MTASK} program before being compiled to byte code. %\mlcomment{Pieter: Refine paragraph about macro expansion and currying/HOF} As an example in \cref{lst_t4t:mtaskTemp} we use \cleaninline{temperature dht >>~.} \cleaninline{setSds localSds} instead of \cleaninline{temperature dht >>~.} \cleaninline{\\temp -> setSds localSds temp}. @@ -1128,13 +1123,13 @@ Alleviating this limitation remains future work. \Gls{MTASK} programs mainly use strict rather than lazy evaluation to minimise the requirement for a variable size heap. This has no significant impact for the \gls{MTASK} programs we have developed here, nor in other \gls{IOT} applications we have engineered. -\Gls{MTASK} abstractions are less easily extended than \gls{ITASK}. For example \gls{ITASK} can be extended with a new combinator that composes a specific set of tasks for some application. +\Gls{MTASK} abstractions are less easily extended than \gls{ITASK}. For example \gls{ITASK} can be extended with a new combinator that composes a specific set of tasks for some application. Without higher order functions the equivalent combinator can often not be expressed in \gls{MTASK}, and adding it to \gls{MTASK} requires extending the \gls{DSL} rather than writing a new definition in it. On the other hand, it is possible to outsource this logic to the \gls{ITASK} program as \gls{MTASK} and \gls{ITASK} tasks are so tightly integrated. -\subsection{The Benefits of a Bare Metal Execution Environment} +\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 microprocessor 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. 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. @@ -1146,16 +1141,16 @@ The \gls{MTASK} \gls{EDSL} and the \gls{MTASK} \gls{RTS} are designed to minimis Intensional analysis of the declarative task description and current progress at run time allow the \gls{RTS} to schedule tasks and maximise idle time. As the \gls{RTS} is the only program running on the device, it can enforce deep sleep and wake up without having to worry about influencing other processes. -The \gls{MTASK} \gls{RTS} has direct control of the peripherals attached to the microcontroller, e.g.\ over \gls{GPIO} pins. There is no interaction with, or permission required from, the \gls{OS}. -Moreover, microcontrollers typically have better support for hardware interrupts, reducing the need to poll peripherals. +The \gls{MTASK} \gls{RTS} has direct control of the peripherals attached to the microprocessor, e.g.\ over \gls{GPIO} pins. There is no interaction with, or permission required from, the \gls{OS}. +Moreover, microprocessors typically have better support for hardware interrupts, reducing the need to poll peripherals. The downside of this direct control is that \gls{CWS} has to handle some exceptions that would otherwise be handled by the \gls{OS} in \gls{CRS} and hence the device management code is longer: 28 versus 20 \gls{SLOC} in \cref{table_t4t:multi}. \subsection{Summary} \Cref{table_t4t:languagecomparison} summarises the differences between the \gls{CLEAN} \gls{IOT} \gls{EDSL} and their host language. -The restrictions imposed by a resource-constrained execution environment on the tierless \gls{IOT} language are relatively minor. Moreover the \gls{MTASK} programming abstraction is broadly compatible with \gls{ITASK}. As a simple example compare the \gls{ITASK} and \gls{MTASK} temperature sensors in \cref{lst_t4t:itaskTempFull,lst_t4t:mtaskTemp}. As a more realistic example, the \gls{MTASK} based \gls{CWS} smart campus implementation is similar to the \gls{ITASK} based \gls{CRS}, and requires less than 10\% additional code: 166 \gls{SLOC} compared with 155 \gls{SLOC} (\cref{table_t4t:multi}). +The restrictions imposed by a resource-constrained execution environment on the tierless \gls{IOT} language are relatively minor. Moreover the \gls{MTASK} programming abstraction is broadly compatible with \gls{ITASK}. As a simple example compare the \gls{ITASK} and \gls{MTASK} temperature sensors in \cref{lst_t4t:itaskTempFull,lst_t4t:mtaskTemp}. As a more realistic example, the \gls{MTASK} based \gls{CWS} smart campus implementation is similar to the \gls{ITASK} based \gls{CRS}, and requires less than 10\% additional code: 166 \gls{SLOC} compared with 155 \gls{SLOC} (\cref{table_t4t:multi}). -Even with these restrictions, \gls{MTASK} programming is at a far higher level of abstraction than almost all bare metal languages, e.g.\ BIT, PICBIT, PICOBIT and Microscheme. That is \gls{MTASK} provides a set of higher order task combinators, shared distributed data stores, etc. (\cref{sec_t4t:mtasks}). Moreover, it seems that common sensor node programs are readily expressed using \gls{MTASK}. In addition to the \gls{CWTS} and \gls{CWS} systems outlined here, other case studies include Arduino examples as well as some bigger tasks~\citep{koopman_task-based_2018,lubbers_writing_2019,LubbersMIPRO}. We conclude that the programming of sensor tasks is well-supported by both \glspl{DSL}. +Even with these restrictions, \gls{MTASK} programming is at a far higher level of abstraction than almost all bare metal languages, e.g.\ BIT, PICBIT, PICOBIT and Microscheme. That is \gls{MTASK} provides a set of higher order task combinators, shared distributed data stores, etc. (\cref{sec_t4t:mtasks}). Moreover, it seems that common sensor node programs are readily expressed using \gls{MTASK}. In addition to the \gls{CWTS} and \gls{CWS} systems outlined here, other case studies include Arduino examples as well as some bigger tasks~\citep{koopman_task-based_2018,lubbers_writing_2019,LubbersMIPRO}. We conclude that the programming of sensor tasks is well-supported by both \glspl{DSL}. \section{Conclusion}% \label{sec_t4t:Conclusion} @@ -1165,7 +1160,7 @@ Even with these restrictions, \gls{MTASK} programming is at a far higher level o We have conducted a systematic comparative evaluation of two tierless language technologies for \gls{IOT} stacks: one for resource-rich, and the other for resource-constrained sensor nodes. The basis is four implementations of a deployed smart campus \gls{IOT} stack: two conventional tiered and \gls{PYTHON}-based stacks, and two tierless \gls{CLEAN} stacks. An operational comparison of implementations demonstrates that they have equivalent functionality, and meet the \gls{UOG} smart campus functional requirements (\cref{sec_t4t:Case}). -We show that \emph{tierless languages have the potential to significantly reduce the development effort for \gls{IOT} systems}. Specifically the tierless \gls{CWS} and \gls{CRS} stacks require far less code, i.e.\ 70\% fewer \gls{SLOC}, than the tiered \gls{PWS} and \gls{PRS} stacks (\cref{table_t4t:multi}). We analyse the code reduction and attribute it to the following three main factors. +We show that \emph{tierless languages have the potential to significantly reduce the development effort for \gls{IOT} systems}. Specifically the tierless \gls{CWS} and \gls{CRS} stacks require far less code, i.e.\ 70\% fewer \gls{SLOC}, than the tiered \gls{PWS} and \gls{PRS} stacks (\cref{table_t4t:multi}). We analyse the code reduction and attribute it to the following three main factors. \begin{enumerate*} \item Tierless developers need to manage less interoperation: \gls{CRS} uses a single \gls{DSL} and paradigm, and \gls{CWS} uses two \glspl{DSL} in a single paradigm and three source code files. In contrast, both \gls{PRS} and \gls{PWS} use at least six languages in two paradigms and spread over at least 35 source code files (\cref{table_t4t:multi,table_t4t:languages,table_t4t:paradigms}). Thus, a tierless stack minimises semantic friction. \item Tierless developers benefit from automatically generated, and hence correct, communication (\cref{lst_t4t:mtaskTemp}), and write 6$\times$ less communication code (\cref{fig_t4t:multipercentage}). @@ -1174,8 +1169,8 @@ We show that \emph{tierless languages have the potential to significantly reduce Our empirical results for \gls{IOT} systems are consistent with the benefits claimed for tierless languages in other application domains. Namely that a tierless language provides a \textit{Higher Abstraction Level}, \textit{Improved Software Design}, and improved \textit{Program Comprehension}~\citep{weisenburger2020survey}. \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 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}. Finally, we contrast community support for the technologies (\cref{sec_t4t:Discussion}). %\pwtcomment{Pieter: please check discussion of ~\citep{weisenburger2020survey} in preceding 2 paragraphs} @@ -1187,8 +1182,8 @@ 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 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}. +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 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 microprocessor 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} @@ -1196,10 +1191,10 @@ This study is based on a specific pair of tierless \gls{IOT} languages, and the This study has explored some, but not all, of the potential benefits of tierless languages for \gls{IOT} systems. An \gls{IOT} system specified as a single tierless program is amenable to a host of programming language technologies. For example, if the language has a formal semantics, as Links, Hop and \gls{CLEAN} tasks do~\citep{cooper2006links,serrano2006hop,plasmeijer_task-oriented_2012}, it is possible to prove properties of the system, e.g.\ \citep{Steenvoorden2019tophat}. As another example program analyses can be applied, and \cref{sec_t4t:characteristics} and~\citep{weisenburger2020survey} outline some of the analyses could be, and in some cases have been, used to improve \gls{IOT} systems. Examples include automatic tier splitting~\citep{10.1145/2661136.2661146}, and controlling information flow to enhance security~\citep{valliappan_towards_2020}. -While offering real benefits for \gls{IOT} systems development, tierless languages also raise some challenges. Programmers must master new tierless programming abstractions, and the semantics of these automatic multi-tier behaviours are necessarily relatively complex. In the \gls{CLEAN} context this entails becoming proficient with the \gls{ITASK} and \gls{MTASK} \glspl{DSL}. Moreover, specifying a behaviour that is not already provided by the tierless language requires either a workaround, or extending a \gls{DSL}. However, implementing the relatively simple smart campus application required no such adaption. Finally, tierless \gls{IOT} technology is very new, and both tool and community support have yet to mature. +While offering real benefits for \gls{IOT} systems development, tierless languages also raise some challenges. Programmers must master new tierless programming abstractions, and the semantics of these automatic multi-tier behaviours are necessarily relatively complex. In the \gls{CLEAN} context this entails becoming proficient with the \gls{ITASK} and \gls{MTASK} \glspl{DSL}. Moreover, specifying a behaviour that is not already provided by the tierless language requires either a workaround, or extending a \gls{DSL}. However, implementing the relatively simple smart campus application required no such adaption. Finally, tierless \gls{IOT} technology is very new, and both tool and community support have yet to mature. -\subsection{Future Work} +\subsection{Future work} This paper is a technology comparison between tiered and tierless technologies. The metrics reported, such as code size, numbers of source code files, and of paradigms are only indirect, although widely accepted, measures of development effort. A more convincing evaluation of tierless technologies could be provided by conducting a carefully designed and substantial user study, e.g.\ using N-version programming. -- 2.20.1