\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}.
+This note is meant to give people who are familiar with the \gls{FP} 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.
Table~\ref{tbl:syn_clean_haskell} shows frequently occuring \gls{CLEAN} language elements on the left side and their \gls{HASKELL} equivalent on the right side.
Obviously, this summary is not exhaustive.
\label{chp:classy_deep_embedding}
\begin{chapterabstract}
- The two flavours of DSL embedding are shallow and deep embedding.
+ The two flavours of \gls{DSL} embedding are shallow and deep embedding.
In functional languages, shallow embedding models the language constructs as functions in which the semantics are embedded.
Adding semantics is therefore cumbersome while adding constructs is a breeze.
Upgrading the functions to type classes lifts this limitation to a certain extent.
As a result, the language constructs are embedded in the semantics, hence adding new language constructs is laborious where adding semantics is trouble free.
This paper shows that by abstracting the semantics functions in deep embedding to type classes, it is possible to easily add language constructs as well.
- So-called classy deep embedding results in DSLs that are extensible both in language constructs and in semantics while maintaining a concrete abstract syntax tree.
+ So-called classy deep embedding results in \glspl{DSL} that are extensible both in language constructs and in semantics while maintaining a concrete abstract syntax tree.
Additionally, little type-level trickery or complicated boilerplate code is required to achieve this.
\end{chapterabstract}
\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.
+The two flavours of \gls{DSL} embedding are deep and shallow embedding~\citep{boulton_experience_1992}.
+In \gls{FP} languages, shallow embedding models language constructs as functions in the host language.
As a result, adding new language constructs---extra functions---is easy.
However, the semantics of the language is embedded in these functions, making it troublesome to add semantics since it requires updating all existing language constructs.
In shallow embedding, abstracting the functions to type classes disentangles the language constructs from the semantics, allowing extension both ways.
This technique is dubbed tagless-final embedding~\citep{carette_finally_2009}, nonetheless it is no silver bullet.
Some semantics that require an intensional analysis of the syntax tree, such as transformation and optimisations, are difficult to implement in shallow embedding due to the lack of an explicit data structure representing the abstract syntax tree.
-The semantics of the DSL have to be combined and must hold some kind of state or context, so that structural information is not lost~\citep{kiselyov_typed_2012}.
+The semantics of the \gls{DSL} have to be combined and must hold some kind of state or context, so that structural information is not lost~\citep{kiselyov_typed_2012}.
\subsection{Research contribution}
This paper shows how to apply the technique observed in tagless-final embedding to deep embedding.
The presented basic technique, christened \emph{classy deep embedding}, does not require advanced type system extensions to be used.
-However, it is suitable for type system extensions such as generalised algebraic data types.
+However, it is suitable for type system extensions such as \glspl{GADT}.
While this paper is written as a literate
-Haskell~\citep{peyton_jones_haskell_2003} program using some minor extensions provided by GHC~\citep{ghc_team_ghc_2021}, the idea is applicable to other languages as well\footnotemark{}.
+\Gls{HASKELL}~\citep{peyton_jones_haskell_2003} program using some minor extensions provided by \gls{GHC}~\citep{ghc_team_ghc_2021}, the idea is applicable to other languages as well\footnotemark{}.
\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}
-Pick a DSL, any DSL, pick the language of literal integers and addition.
+Pick a \gls{DSL}, any \gls{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.}.
| Sub_0 Expr_0 Expr_0
\end{lstHaskellLhstex}
-Extending the DSL with language constructs exposes the Achilles' heel of deep embedding.
+Extending the \gls{DSL} with language constructs exposes the Achilles' heel of deep embedding.
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.
\end{lstHaskellLhstex}
\section{Lifting the backends}%
-Let us rethink the deeply embedded DSL design.
+Let us rethink the deeply embedded \gls{DSL} design.
Remember that in shallow embedding, the semantics are embedded in the language construct functions.
Obtaining extensibility both in constructs and semantics was accomplished by abstracting the semantics functions to type classes, making the constructs overloaded in the semantics.
In deep embedding, the constructs are embedded in the semantics functions instead.
e3 = neg_4 (Lit_4 42 `sub_4` Lit_4 38) `Add_4` Lit_4 1
\end{lstHaskellLhstex}
-\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~\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.
+\section{\texorpdfstring{\Acrlongpl{GADT}}{Generalised algebraic data types}}%
+\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}.
+Leveraging \glspl{GADT}, deeply embedded \glspl{DSL} can be made statically type safe even when different value types are supported.
+Even when \glspl{GADT} 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 \glspl{GADT} (see \cref{sec:cde:related}), classy deep embedding does.
+Generalising the data structure of our \gls{DSL} is fairly straightforward and to spice things up a bit, we add an equality and boolean not language construct.
+To make the existing \gls{DSL} constructs more general, we relax the types of those constructors.
For example, operations on integers now work on all numerals instead.
-Moreover, the \haskelllhstexinline{Lit_g} constructor can be used to lift values of any type to the DSL domain as long as they have a \haskelllhstexinline{Show} instance, required for the printer.
+Moreover, the \haskelllhstexinline{Lit_g} constructor can be used to lift values of any type to the \gls{DSL} domain as long as they have a \haskelllhstexinline{Show} instance, required for the printer.
Since some optimisations on \haskelllhstexinline{Not_g} remove constructors and therefore use cross-extensional pattern matches, \haskelllhstexinline{Typeable} constraints are added to \haskelllhstexinline{a}.
Furthermore, because the optimisations for \haskelllhstexinline{Add_g} and \haskelllhstexinline{Sub_g} are now more general, they do not only work for \haskelllhstexinline{Int}s but for any type with a \haskelllhstexinline{Num} instance, the \haskelllhstexinline{Eq} constraint is added to these constructors as well.
Finally, not to repeat ourselves too much, we only show the parts that substantially changed.
not_g e = Ext_g gdict (Not_g e)
\end{lstHaskellLhstex}
-Upgrading the semantics type classes to support GADTs is done by an easy textual search and replace.
+Upgrading the semantics type classes to support \glspl{GADT} is done by an easy textual search and replace.
All occurrences of \haskelllhstexinline{v} are now parametrised by type variable \haskelllhstexinline{a}:
\begin{lstHaskellLhstex}
\section{Related work}%
\label{sec:cde:related}
-Embedded DSL techniques in functional languages have been a topic of research for many years, thus we do not claim a complete overview of related work.
+Embedded \gls{DSL} techniques in functional languages have been a topic of research for many years, thus we do not claim a complete overview of related work.
Clearly, classy deep embedding bears most similarity to the \emph{Datatypes \`a la Carte}~\citep{swierstra_data_2008}.
In Swierstra's approach, semantics are lifted to type classes similarly to classy deep embedding.
Commonly used intepretations are printing, compiling, simulating, optimising, verifying, proving the program\etc.
There are two main flavours of embedding \glspl{DSL}, deep---also called tagged---embedding and shallow---also called tagless---embedding.
+Most importantly, the two flavours differ on two axes: extensibility of language constructs and extensibility of interpretations.
+\todo{elaborate}
+
\Cref{sec:deep_embedding} shows the basics of deep embedding.
\Cref{sec:shallow_embedding} shows the basics of shallow embedding including tagless embedding.
\Cref{sec:compare_embedding} compares the embedding technique.
-Both flavours have their strengths and weaknesses and both flavours can be improved in order to mitigate (some of the) downsides.
-
-\begin{table}[ht]
- \begin{threeparttable}[b]
- \caption{Comparison of embedding techniques, adapted from \citet[Sec.~3.6]{sun_compositional_2022}}%
- \label{tbl:dsl_comparison}
- \begin{tabular}{lllllll}
- \toprule
- & Shallow & Deep & Hybrid & Poly & Comp. & Classy\\
- \midrule
- Transcoding free & yes & yes & no & yes & yes & yes\\
- Linguistic reuse & yes & no & partly\tnote{1} & yes & yes & no?\\
- Extend constructs & yes & no & partly\tnote{1} & yes & yes & yes\\
- Extend interpretations & no & yes & yes & yes & yes & yes\\
- Transformations & no & yes & yes & maybe\tnote{2} & maybe\tnote{2} & yes\\
- Modular dependencies & no & maybe & maybe & yes & yes & yes\\
- Nested pattern matching & no & yes & yes & no & maybe & maybe\tnote{3}\\
- Type safe & yes & maybe & no & yes & yes & yes\\
- \bottomrule
- \end{tabular}
- \begin{tablenotes}
- \item [1] Only in the shallowly embedded part.
- \item [2] Transformations require some ingenuity and are sometimes awkward to write.
- \item [3] It requires some---safe---form of dynamic typing.
- \end{tablenotes}
- \end{threeparttable}
-\end{table}
-
-In the following sections both techniques are explained.
-As an example, a simple language with integers, booleans and some arithmetic operators is used as a running example.
+In the following sections the basics of both techniques are explained.
+A simple language with integers, booleans and some arithmetic operators is used as a running example.
\section{Deep embedding}\label{sec:deep_embedding}
In a deeply embedded \gls{DSL}, the language terms are represented as data type{(s)} in the host language.
Therefore, interpretations of the terms are functions that operate on these data types.
-Definition~\ref{lst:exdeep} shows an implementation for the example \gls{DSL}.
+\Cref{lst:exdeep} shows an implementation for the example \gls{DSL}.
\begin{lstHaskell}[label={lst:exdeep},caption={A deeply embedded expression \gls{DSL}.}]
data Value = I Int | B Bool
eval :: Expr -> Value
eval (Lit i) = i
eval (Plus l r) = case (eval l, eval r) of
- (Lit (I l), Lit (I r)) -> I (l+r))
+ (Lit (I l), Lit (I r)) -> I (l+r))
(l, r) -> error ("Can't add " ++ show l ++ " to " ++ show r)
eval (Eq l r) = case (eval l, eval r) of
- (Lit (I l), Lit (I r)) -> B (l==r)
- (Lit (B l), Lit (B r)) -> B (l==r)
+ (Lit (I l), Lit (I r)) -> B (l==r)
+ (Lit (B l), Lit (B r)) -> B (l==r)
(l, r) -> error ("Can't compare " ++ show l ++ " to " ++ show r)
\end{lstHaskell}
-\subsection{Deep embedding with \texorpdfstring{\acrshortpl{GADT}}{GADTs}}
+\subsection{\texorpdfstring{\Acrlongpl{GADT}}{Generalised algebraic data types}}
Deep embedding has the advantage that it is easy to build and views are easy to add.
On the downside, the expressions created with this language are not necessarily type-safe.
-In the given language it is possible to create an expression such as \haskellinline{Plus (LitI 4) (LitB True)} that adds a boolean to an integer.
+In the given language it is possible to create an expression such as \haskellinline{LitI 4 `Plus` LitB True} that adds a boolean to an integer.
Extending the \gls{ADT} is easy and convenient but extending the views accordingly is tedious since it has to be done individually for all views.
The first downside of this type of \gls{EDSL} can be overcome by using \glspl{GADT}~\citep{cheney_first-class_2003}.
\Cref{lst:exdeepgadt} shows the same language, but type-safe with a \gls{GADT}.
\glspl{GADT} are not supported in the current version of \gls{CLEAN} and therefore the syntax is hypothetical (See \todo{insert link to appendix}).
-However, it has been shown that \glspl{GADT} can be simulated using bimaps or projection pairs~\citep{cheney_first-class_2003}.
+However, it has been shown that \glspl{GADT} can be simulated using bimaps or projection pairs~\citep[Sec.~2.2]{cheney_lightweight_2002}.
Unfortunately the lack of extendability remains a problem.
If a language construct is added, no compile time guarantee can be given that all views support it.
eval (Eq l r) = eval l == eval r
\end{lstHaskell}
-\section{Shallow embedding}
+\section{Shallow embedding}\label{sec: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 \cref{lst:exshallow}.
-Note that much of the internals of the language can be hidden using monads.
+Note that the internals of the language could have been hidden using a reader monad.
\begin{lstHaskell}[label={lst:exshallow}, caption={A minimal shallow \gls{EDSL}.}]
type Env = String -> Int
type DSL a = Env -> a
-Lit :: a -> DSL a
-Lit x = \e->x
+lit :: a -> DSL a
+lit x = \e->x
-Var :: String -> DSL Int
-Var i = \e->retrEnv e i
+var :: String -> DSL Int
+var i = \e->retrEnv e i
-Plus :: DSL Int -> DSL Int -> DSL Int
-Plus x y = \e->x e + y e
+plus :: DSL Int -> DSL Int -> DSL Int
+plus x y = \e->x e + y e
-Eq :: Eq a => DSL a -> DSL a -> DSL Bool
-Eq x y = \e->x e == y e
+eq :: Eq a => DSL a -> DSL a -> DSL Bool
+eq x y = \e->x e == y e
\end{lstHaskell}
One of the advantages of shallowly embedding a language in a host language is its extendability.
It is very easy to add functionality because the compile time checks of the host language guarantee whether or not the functionality is available when used.
-Moreover, the language is type safe as it is directly typed in the host language, i.e.\ \cleaninline{Lit True +. Lit 4} is rejected.
+For example, adding a new construct---such as subtraction---is done as follows:
+
+\begin{lstHaskell}[label={lst:exshallowsubst},caption={Adding subtraction to the shallow \gls{EDSL}.}]
+sub :: DSL Int -> DSL Int -> DSL Int
+sub x y = \e->x e - y e
+\end{lstHaskell}
+
+Moreover, the language is type safe as it is directly typed in the host language, i.e.\ \haskellinline{lit True `plus` lit 4} is rejected.
Another advantage is the intimate link with the host language, allowing for a lot more linguistic reuse such as the support of implicit sharing~\cite{krishnamurthi_linguistic_2001}.
The downside of this method is extending the language with views.
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.
+The only way of achieving this is by reimplementing all functions so that they run all backends at the same time or to create a single interpretation that produces a fold function~\citep{gibbons_folding_2014}.
\subsection{Tagless-final embedding}\label{ssec:tagless}
+By lifting the functions representing the \gls{DSL} terms to type classes, interpretations can be added.
+This technique is called tagless-final---or class-based shallow---embedding.
+The interface for the \gls{DSL} looks as follows:
+
+\begin{lstHaskell}[label={lst:extagless},caption={A minimal tagless-final \gls{EDSL}.}]
+class DSL v where
+ lit :: a -> v a
+ var :: String -> v a
+ plus :: v Int -> v Int -> v Int
+ eq :: Eq a => v a -> v a -> v Bool
+\end{lstHaskell}
+
+An interpretation of this view is a data type that implements the type class.
+
+\begin{lstHaskell}[label={lst:extagless},caption={A minimal tagless-final \gls{EDSL}.}]
+data Print a = P {runPrint :: String}
+instance DSL Print where
+ lit a = P (show a)
+ var i = P i
+ plus x y = P ("(" ++ runPrint x ++ "+" ++ runPrint y ++ ")"
+ eq x y = P ("(" ++ runPrint x ++ "==" ++ runPrint y ++ ")"
+\end{lstHaskell}
+Adding a language construct---e.g.\ subtraction---is a easy as adding a type class and providing instances for interpretations.
-\section{Comparison}
-\todo{cite compositional DSLs}
+\begin{lstHaskell}[label={lst:extaglesssubt},caption={Adding subtraction to the shallow \gls{EDSL}.}]
+class Sub v where
+ sub :: v Int -> v Int -> v Int
+
+instance Sub Print where
+ sub x y = P ("(" ++ runPrint x ++ "-" ++ runPrint y ++ ")"
+\end{lstHaskell}
+
+Adding an interpretation means adding a data type and providing instances for the language constructs.
+
+\begin{lstHaskell}[label={lst:extagless},caption={An evaluator interpretation of the minimal tagless-final \gls{EDSL}.}]
+data Eval a = Eval {runEval :: Env -> a}
+
+instance DSL v where
+ lit a = Eval (\_->a)
+ var i = Eval (\e->retrEnv e i)
+ plus x y = Eval (\e->runEval x e + runEval y e)
+ eq x y = Eval (\e->runEval x e == runEval y e)
+
+instance Sub Eval where
+ sub x y = Eval (\e->runEval x e - runEval y e)
+\end{lstHaskell}
+
+\section{Comparison}\label{sec:compare_embedding}
+Both flavours have their strengths and weaknesses and both flavours can be improved in order to mitigate (some of the) downsides.
+
+\begin{table}[ht]
+ \begin{threeparttable}[b]
+ \caption{Comparison of embedding techniques, adapted from \citet[Sec.~3.6]{sun_compositional_2022}}%
+ \label{tbl:dsl_comparison}
+ \begin{tabular}{lllllll}
+ \toprule
+ & Shallow & Deep & Hybrid & Poly & Comp. & Classy\\
+ \midrule
+ Transcoding free & yes & yes & no & yes & yes & yes\\
+ Linguistic reuse & yes & no & partly\tnote{1} & yes & yes & no?\\
+ Extend constructs & yes & no & partly\tnote{1} & yes & yes & yes\\
+ Extend interpretations & no & yes & yes & yes & yes & yes\\
+ Transformations & no & yes & yes & maybe\tnote{2} & maybe\tnote{2} & yes\\
+ Modular dependencies & no & maybe & maybe & yes & yes & yes\\
+ Nested pattern matching & no & yes & yes & no & maybe & maybe\tnote{3}\\
+ Type safe & yes & maybe & no & yes & yes & yes\\
+ \bottomrule
+ \end{tabular}
+ \begin{tablenotes}
+ \item [1] Only in the shallowly embedded part.
+ \item [2] Transformations require some ingenuity and are sometimes awkward to write.
+ \item [3] It requires some---safe---form of dynamic typing.
+ \end{tablenotes}
+ \end{threeparttable}
+\end{table}
\input{subfilepostamble}
\end{document}
\chapter[First-class data types in shallow \acrshortpl{EDSL} using metaprogramming]{First-class data types in shallow \acrlongpl{EDSL} using metaprogramming}%
\label{chp:first-class_datatypes}%
\begin{chapterabstract}
- Functional programming languages are excellent candidates for hosting \glspl{EDSL} because of their rich type systems, minimal syntax, and referential transparency.
+ \Gls{FP} languages are excellent candidates for hosting \glspl{EDSL} because of their rich type systems, minimal syntax, and referential transparency.
However, data types defined in the host language are not automatically available in the embedded language.
To do so, all the operations on the data type must be ported to the \gls{EDSL} resulting in a lot of boilerplate.
\end{chapterabstract}
\section{Introduction}
-Functional programming languages are excellent candidates for hosting \glspl{EDSL} because of their rich type systems, minimal syntax, and referential transparency.
+\Gls{FP} languages are excellent candidates for hosting \glspl{EDSL} because of their rich type systems, minimal syntax, and referential transparency.
By expressing the language constructs in the host language, the parser, the type checker, and the run time can be inherited from the host language.
Unfortunately, data types defined in the host language are not automatically available in the \gls{EDSL}.
-The two main strategies for embedding \glspl{DSL} in a functional language are deep embedding (also called initial) and shallow embedding (also called final).
+The two main strategies for embedding \glspl{DSL} in a \gls{FP} language are deep embedding (also called initial) and shallow embedding (also called final).
Deep embedding represents the constructs in the language as data types and the semantics as functions over these data types.
This makes extending the language with new semantics effortless: just add another function.
In contrast, adding language constructs requires changing the data type and updating all existing semantics to support this new construct.
Willis et al.\ used \gls{TTH} to remove the overhead of parsing combinators~\citep{willis_staged_2020}.
\section{Discussion}
-Functional programming languages are especially suitable for embedding \glspl{DSL} but adding user-defined data types is still an issue.
+\Gls{FP} languages are especially suitable for embedding \glspl{DSL} but adding user-defined data types is still an issue.
The tagless-final style of embedding offers great modularity, extensibility and flexibility.
However, user-defined data types are awkward to handle because the built-in operations on them---construction, deconstruction and constructor tests---are not inherited from the host language.
By giving an implementation, we showed how to create a \gls{TH} function that will splice the required class definitions and view instances.
\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{CEFP}{CEFP}{central European summer school of \acrlong{FP}}
\newacronym{CRS}{CRS}{\gls{CLEAN} Raspberry Pi system}
\newacronym{CRTS}{CRTS}{\gls{CLEAN} Raspberry Pi temperature sensor}
\newacronym{CWS}{CWS}{\gls{CLEAN} wemos system}
}
\newglossaryentry{CLEAN}{%
name=Clean,
- description={Clean \acrlong{LEAN}, a pure lazy functional programming language based on graph rewriting}
+ description={Clean \acrlong{LEAN}, a pure lazy \acrlong{FP} language based on graph rewriting}
}
\newglossaryentry{HASKELL}{%
name=Haskell,
- description={is a pure lazy functional programming language designed by a committe as a concept language}
+ description={is a pure lazy \acrlong{FP} language designed by a committe as a concept language}
}
\newglossaryentry{HASKELL98}{%
name=Haskell98,
The \gls{UOD} from the business layer is explicitly and separately modelled by the relations that exist in the functions of the host language.
\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}.
+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 \gls{FP} 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}.
The thesis wraps up with \cref{chp:conclusion} that provides a conclusion and an outlook on future work.
\subsection*{\nameref{prt:dsl}}
-This movement is a cumulative---paper-based---movement that focusses on techniques for embedding \glspl{DSL} in functional programming lanugages.
+This movement is a cumulative---paper-based---movement that focusses on techniques for embedding \glspl{DSL} in \gls{FP} lanugages.
After reading the first chapter, subsequent chapters in this movement are readable as independently.
\subsubsection*{\fullref{chp:dsl_embedding_techniques}}
{}
% Hyperlinks and metadata
-\usepackage[pagebackref]{hyperref} % hyperlinks
+\usepackage[pdflang={en-GB},pagebackref]{hyperref} % hyperlinks
\renewcommand*{\backref}[1]{}
\renewcommand*{\backrefalt}[4]{[{%
\ifcase #1 not cited.\or p.~#2.\else pp. #2.\fi%chktex 1
\end{chapterabstract}
The \gls{MTASK} system is a \gls{TOP} programming environment for programming microprocessors.
-It is implemented as an \gls{EDSL} in \gls{CLEAN} using class-based---or tagless-final---embedding (See \cref{ssec:tagless}).
+It is implemented as an\gls{EDSL} in \gls{CLEAN} using class-based---or tagless-final---embedding (See \cref{ssec:tagless}).
Due to the nature of the embedding technique, it is possible to have multiple interpretations of---or views on---programs written in the \gls{MTASK} language.
The following interpretations are available for \gls{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
+The \gls{MTASK} language is a multi-view \gls{DSL}, i.e.\ there are multiple interpretations possible for a single \gls{MTASK} term.
+Using the byte code compiler (\cleaninline{BCInterpret}) \gls{DSL} interpretation, \gls{MTASK} tasks are fully integrated in \gls{ITASK} and executed as if they were regular \gls{ITASK} tasks and communicate using \gls{ITASK} \glspl{SDS}.
+\Gls{MTASK} devices contain a domain-specific \gls{OS} (\gls{RTS}) and are little \gls{TOP} servers in their own respect, being able to execute tasks.
+\Cref{fig:mtask_integration} shows the architectural layout of a typical \gls{IOT} system created with \gls{ITASK} and \gls{MTASK}.
+The entire system is written as a single \gls{CLEAN} specification where multiple tasks are executed at the same time.
+Tasks can access \glspl{SDS} according to many-to-many communication and multiple clients can work on the same task.
+Devices are integrated into the system using the \cleaninline{widthDevice} function (see \cref{sec:withdevice}).
+Using \cleaninline{liftmTask}, \gls{MTASK} tasks are lifted to a device (see \cref{sec:liftmtask}).
+\Gls{ITASK} \glspl{SDS} are lifted to the \gls{MTASK} device using \cleaninline{liftsds} (see \cref{sec:liftmtask}).
\begin{figure}[ht]
\centering
\label{fig:mtask_integration}
\end{figure}
-\section{Devices}
+\section{Devices}\label{sec:withdevice}
\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.
withDevice :: (a (MTDevice -> Task b) -> Task b) | iTask b & channelSync, iTask a
\end{lstClean}
-\section{Lifting tasks}
+\section{Lifting tasks}\label{sec:liftmtask}
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}}
+\section{Lifting \texorpdfstring{\acrlongpl{SDS}}{shared data sources}}\label{sec:liftsds}
\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)))
\node (CN) [client,inner sep=0pt,draw,right=of CD] {Client\textsubscript{n}};
% line between server and browser
- \draw [dotted] ([xshift=-5em,yshift=-.8em]C1.south west) node[left,above]{browser} node[left,below]{server(s)} -- ([xshift=3em,yshift=-.8em]CN.south east);
+ \draw [dotted] ([xshift=-7em,yshift=-.8em]C1.south west) node[left,above]{browser} node[left,below]{server(s)} -- ([xshift=5em,yshift=-.8em]CN.south east);
- \node[task,inner sep=-4.5pt,below=of C1,fill=white,xshift=4.5em,yshift=-4.5em] (Tn) {Task\textsubscript{n}};
- \node[task,inner sep=-4.5pt,below=of C1,fill=white,xshift=3em,yshift=-3.0em] (Td) {\ldots};
- \node[task,inner sep=-4.5pt,below=of C1,fill=white,xshift=1.5em,yshift=-1.5em] (T2) {Task\textsubscript{2}};
- \node[task,inner sep=-4.5pt,below=of C1,fill=white] (T1) {Task\textsubscript{1}};
+ \node[task,inner sep=-4.5pt,below=of C1,fill=white,xshift=6.5em,yshift=-4.5em] (Tn) {Task\textsubscript{n}};
+ \node[task,inner sep=-4.5pt,below=of C1,fill=white,xshift=5em,yshift=-3.0em] (Td) {\ldots};
+ \node[task,inner sep=-4.5pt,below=of C1,fill=white,xshift=3.5em,yshift=-1.5em] (T2) {Task\textsubscript{2}};
+ \node[task,inner sep=-4.5pt,below=of C1,fill=white,xshift=2em] (T1) {Task\textsubscript{1}};
\node[share,node distance=6em,right=of T1,fill=white,xshift=4.5em,yshift=-4.5em] (Sn) {SDS\textsubscript{n}};
\node[share,node distance=6em,right=of T1,fill=white,xshift=3em,yshift=-3.0em] (Sd) {\ldots};
\node[share,node distance=6em,right=of T1,fill=white,xshift=1.5em,yshift=-1.5em] (S2) {SDS\textsubscript{2}};
\node[share,node distance=6em,right=of T1,fill=white] (S1) {SDS\textsubscript{1}};
+ \node[fit={(T1)(Sn)},draw] (TS) {};
+
% line between client and server
- \draw [dotted] ([xshift=-5em,yshift=-11em]C1.south west) node[left,above]{server(s)} node[left,below]{device(s)} -- ([xshift=3em,yshift=-11em]CN.south east);
+ \draw [dotted] ([xshift=-7em,yshift=-11em]C1.south west) node[left,above]{server(s)} node[left,below]{device(s)} -- ([xshift=5em,yshift=-11em]CN.south east);
% device 1
\node[task,text width=1.5em,inner sep=-4.5pt,node distance=12em,below=of C1,double copy shadow={shadow xshift=.4em,shadow yshift=-.4em},fill=white] (D1T1) {};
\node[share,text width=.3em,text height=.8ex,right=of D1T1,double copy shadow={shadow xshift=.4em,shadow yshift=-.4em},fill=white] (D1S1) {};
- \node[draw,fit={(D1S1)(D1T1)([shift={(1.2em,-1.2em)}]D1S1.south east)},label=below:{\scriptsize mTask device\textsubscript{1}}] {};
+ \node[draw,fit={(D1S1)(D1T1)([shift={(1.2em,-1.2em)}]D1S1.south east)},label=below:{\scriptsize mTask device\textsubscript{1}}] (D1) {};
% device 2
\node[task,text width=1.5em,inner sep=-4.5pt,node distance=12em,below=of C2,double copy shadow={shadow xshift=.4em,shadow yshift=-.4em},fill=white] (D2T1) {};
\draw [<->] (CN) -- (T1);
\draw [<->] (T1) -- (S1);
- \draw [<->] (T1) -- (S2);
- \draw [<->] (T1) -- (Sn);
+% \draw [<->] (T1) -- (S2);
+% \draw [<->] (T1) -- (Sn);
- \draw [<->] (Tn) -- node [midway,above,sloped] {\scriptsize\texttt{liftmTask}} (D1T1);
- \draw [<->] (Sn) -- node [midway,above,sloped] {\scriptsize\texttt{liftsds}} (D1S1);
+ \draw [<->] (Tn) -- node [midway,above,fill=white] {\small\texttt{liftmTask}} (D2T1);
+ \draw [<->] (Sn) -- node [midway,above,fill=white] {\small\texttt{liftsds}} (D2S1);
\draw [<->] (D1T1) -- (D1S1);
+ \draw [<->] (D2T1) -- (D2S1);
+ \draw [<->] (DNT1) -- (DNS1);
+ \draw [<->] (D1.north) to [in=180,out=135] node [midway,above,fill=white] {\small\texttt{withDevice}} (TS.west);
\end{tikzpicture}
\end{document}
To make this paper self-contained we provide a concise overview of \gls{CLEAN}, \gls{TOP}, and \gls{IOT} programming in \gls{ITASK} and \gls{MTASK}. The minor innovations reported here are the interface to the \gls{IOT} sensors, and the \gls{CLEAN} port for the Raspberry Pi.
-\Gls{CLEAN} is a statically typed functional programming language similar to \gls{HASKELL}: both languages are pure and non-strict~\citep{achten_clean_2007}.
+\Gls{CLEAN} is a statically typed \gls{FP} 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.
%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, \gls{FP} languages are generally more concise than most other programming languages because their powerful abstractions like higher-order and/or polymorphic functions require less code to describe a computation.
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}.
In contrast, tierless languages are far less mature than the languages used in tiered stacks, and far less widely adopted.
This means that for \gls{CWS} and \gls{CRS} there are fewer tools, a far smaller developer community, and less training material available.
-\Gls{CWS} and \gls{CRS} are both written in \glspl{DSL} embedded in \gls{CLEAN}, a fairly stable industrial-grade but niche functional programming language.
+\Gls{CWS} and \gls{CRS} are both written in \glspl{DSL} embedded in \gls{CLEAN}, a fairly stable industrial-grade but niche \gls{FP} language.
The \glspl{DSL} are implemented in \gls{CLEAN} but require experimental compiler extensions that are often undocumented.
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.