1 \documentclass[../thesis.tex
]{subfiles
}
3 \input{subfilepreamble
}
7 \ifSubfilesClassLoaded{\appendix}{}
8 \chapter{Clean for Haskell programmers
}%
9 \label{chp:clean_for_haskell_programmers
}
11 This appendix is meant give people who are familiar with the
\gls{FP
} language
\gls{HASKELL
} a concise overview of the programming language
\gls{CLEAN
} and how it differs from
\gls{HASKELL
}.
12 The goal is to support the reader when reading
\gls{CLEAN
} code.
13 \Cref{tbl:syn_clean_haskell
} shows frequently occurring
\gls{CLEAN
} language elements on the left side and their
\gls{HASKELL
} equivalent on the right side.
14 Obviously, this summary is not exhaustive.
15 Some
\gls{CLEAN
} language elements that are not easily translatable to
\gls{HASKELL
} and thus do not occur in the summary following below.
16 We hope you enjoy these notes and that it aids you in reading
\gls{CLEAN
} programs.
18 \Gls{CLEAN
}---acronym for Clean Language of East-Anglia and Nijmegen
\citep{barendregt_towards_1987
}---, was originally designed as a
\gls{GRS
} core language but quickly served as an intermediate language for other functional languages
\citep{brus_clean_1987
}.
19 In the early days it has also been called
\emph{Concurrent
} \gls{CLEAN
} \citep{nocker_concurrent_1991
} but these days the language has no support for concurrency anymore.
20 Fast-forward thirty years,
\gls{CLEAN
} is now a robust language with state-of-the-art features and is actually used in industry as well as academia---albeit in select areas of the world.
22 Initially, when it was used mostly as an intermediate language, it had a fairly spartan syntax.
23 However, over the years, the syntax got friendlier and it currently it looks a lot like
\gls{HASKELL
}.
24 In the past, a
\emph{double-edged
} fronted even existed that allowed
\gls{CLEAN
} to be extended with
\gls{HASKELL
}98 syntax and vice versa
\citep{van_groningen_exchanging_2010
}, however this frontend is no longer maintained.
25 This chapter gives only a brief syntactical and functional comparison.
26 A complete specification of the
\gls{CLEAN
} language can be found in the latest language
report \citep{plasmeijer_clean_2021
}.
27 Much of this is based on work by Achten, although that was based on
\gls{CLEAN
} 2.1 and
\gls{HASKELL
}98 \citep{achten_clean_2007
}.
28 When
\gls{HASKELL
} is mentioned we actually mean
\gls{GHC
}'s
\gls{HASKELL
}\footnote{If an extension is enabled, a footnote is added stating that
\GHCmod{SomeExtension
} is required.
} and by
\gls{CLEAN
} we mean
\gls{CLEAN
} 3.1's compiler with the
\gls{ITASK
} extensions.
32 \Gls{CLEAN
} has separate implementation and definition modules.
33 The definition module contains the class definitions, instances, function types and type definitions (possibly abstract).
34 Implementation modules contain the function implementations as well.
35 This means that only what is defined in the definition module is exported in
\gls{CLEAN
}.
36 This differs greatly from
\gls{HASKELL
}, as there is only a module file there.
37 Choosing what is exported in
\gls{HASKELL
} is done using the
\haskellinline{module Mod(...)
} syntax.
39 \subsection{Strictness
}
40 In
\gls{CLEAN
}, by default, all expressions are evaluated lazily.
41 Types can be annotated with a strictness attributes (
\cleaninline{!
}), resulting in the values being evaluated to head-normal form before the function is entered.
42 In
\gls{HASKELL
}, in patterns, strictness is enforced using
\haskellinline{!
}\requiresGHCmod{BangPatterns
}.
43 Within functions, the strict let (
\cleaninline{#!
}) is used to force evaluate an expression, in
\gls{HASKELL
} \haskellinline{seq
} or
\haskellinline{\$!
} is used for this.
45 \subsection{Uniqueness typing
}
46 Types in
\gls{CLEAN
} may be
\emph{unique
}, which means that instances of the type cannot be shared
\citep{barendsen_uniqueness_1996
}.
47 The uniqueness type system allows the compiler to generate efficient code because unique data structures can be destructively updated.
48 Furthermore, uniqueness typing serves as a model for side effects as well
\citep{achten_high_1993,achten_ins_1995
}.
49 \Gls{CLEAN
} uses the
\emph{world-as-value
} paradigm where
\cleaninline{World
} represents the external environment and is always unique
\citep{backus_introduction_1990
}.
50 A program with side effects is characterised by a
\cleaninline{Start :: *World -> *World
} start function.
51 In
\gls{HASKELL
}, interaction with the world is done using the
\haskellinline{IO
} monad
\citep{peyton_jones_imperative_1993
}.
52 An
\haskellinline{IO
} monad could very well be---and actually is---implemented in
\gls{CLEAN
} using a state monad with the
\cleaninline{World
} as a state.
53 Besides marking types as unique, it is also possible to mark them with uniqueness attributes variables
\cleaninline{u:
} and define constraints on them.
54 For example, to make sure that an argument of a function is at least as unique as another argument.
55 Finally, using
\cleaninline{.
} (a full stop), it is possible to state that several variables are equally unique.
56 Uniqueness is propagated automatically in function types but must be marked manually in data types.
57 Examples can be seen in
\cref{lst:unique_examples
}.
59 \begin{lstClean
}[label=
{lst:unique_examples
},caption=
{Examples of uniqueness annotations.
}]
60 f :: *a -> *a // f works on unique values only
61 f :: .a -> .a // f works on unique and non-unique values
62 f :: v:a u:b -> u:b,
[v<=u
] // f works when a is less unique than b
64 %f :: (Int, *World) -> *World // The uniqueness is propagated automatically (i.e. *(Int, *World)))
65 %:: T = T *(Int, *World) // Writing :: T = T (Int, *World) won't work
66 %:: T = T (Int -> *(*World -> *World)) // Writing :: T = T (Int *World -> *World) won't work
68 \subsection{Expressions
}
69 Patterns in
\gls{CLEAN
} can be used as predicates as well
\citep[\citesection{3.4.3}]{plasmeijer_clean_2021
}.
70 Using the
\cleaninline{=:
} operator, a value is tested against a pattern.
71 Variable names are not allowed but wildcard patterns
\cleaninline{\_} are.
73 \begin{lstClean
}[label=
{lst:matches_pattern_expression
},caption=
{Examples of
\emph{matches pattern
} expressions.
}]
80 ifAB x ifa ifb = if (x =: (A _)) ifa ifb
83 Due to the nature of uniqueness typing, many functions in
\gls{CLEAN
} are state transition functions with possibly unique states.
84 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.
85 \Cref{lst:let_before
} shows an example of the usage of the
\emph{let before
} construct (adapted from
\citep[\citesection{3.5.4}]{plasmeijer_clean_2021
}).
87 \begin{lstClean
}[label=
{lst:let_before
},caption=
{Let before expression example.
}]
88 readChars :: *File -> (
[Char
], *File)
90 # (ok, char, file) = freadc file
92 # (chars, file) = readChars file
93 = (
[char:chars
], file)
97 Polytypic functions
\citep{jeuring_polytypic_1996
}---also known as generic or kind-indexed functions---are built into
\gls{CLEAN
} \citep[\citesection{7.1}]{plasmeijer_clean_2021
}\citep{alimarine_generic_2005
} whereas in
\gls{HASKELL
} they are implemented as a library
\citep[\citesection{6.19.1}]{ghc_team_ghc_2021
}.
98 The syntax of the built-in generics of
\gls{CLEAN
} is very similar to that of Generic H$
\forall$skell
\citep{hinze_generic_2003
}.
99 %When calling a generic function, the kind must always be specified and depending on the kind, the function may require more arguments.
101 For example, defining a generic equality is done as in
\cref{lst:generic_eq
}.
102 \cleaninputlisting[firstline=
4,label=
{lst:generic_eq
},caption=
{Generic equality function
}]{lst/generic_eq.icl
}
104 Metadata about the types is available using the
\cleaninline{of
} syntax that gives the function access to metadata records, as can be seen in
\cref{lst:generic_print
} showing a generic print function. This abundance of metadata allows for very complex generic functions that near the expression level of template metaprogramming (see
\cref{chp:first-class_datatypes,sec:ccodegen
}).
105 \cleaninputlisting[language=Clean,firstline=
4,label=
{lst:generic_print
},caption=
{Generic print function
}]{lst/generic_print.icl
}
108 \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
}.
109 While
\glspl{GADT
} are not natively supported in
\gls{CLEAN
}, they can be simulated using embedding-projection pairs or equivalence types
\citep[\citesection{2.2}]{cheney_lightweight_2002
}.
110 To illustrate this,
\cref{lst:gadt_haskell
} shows a
\gls{GADT
} in
\gls{HASKELL
}\requiresGHCmod{GADTs
} that can be implemented as in
\cref{lst:gadt_clean
}.
112 \haskellinputlisting[firstline=
4,label=
{lst:gadt_haskell
},caption=
{Expression
\gls{GADT
}.
}]{lst/expr_gadt.hs
}
113 \cleaninputlisting[firstline=
4,lastline=
24,label=
{lst:gadt_clean
},caption=
{Expression
\gls{GADT
} using equivalence types.
}]{lst/expr_gadt.icl
}
117 \lstset{basicstyle=
\tt\footnotesize}
118 \begin{longtable
}{p
{.46\linewidth}p
{.46\linewidth}}
119 \caption[]{Syntactical differences between
\gls{CLEAN
} and
\gls{HASKELL
}.
}%
120 \label{tbl:syn_clean_haskell
}\\
122 \gls{CLEAN
} &
\gls{HASKELL
}\\
125 \caption[]{Syntactical differences between
\gls{CLEAN
} and
\gls{HASKELL
}. (continued)
}\\
127 \gls{CLEAN
} &
\gls{HASKELL
}\\
132 \multicolumn{2}{c
}{Comments
}\\
134 \cleaninline{// single line
} &
\haskellinline{-- single line
}\\
135 \cleaninline{/* multi line /* nested */ */
} &
\haskellinline{\
{- multi line \
{- nested -\
} \
}}\\
138 \multicolumn{2}{c
}{Imports
}\\
140 \cleaninline{import Mod => qualified f1, :: t
} &
\haskellinline{import qualified Mod (f1, t)
}\\
141 &
\haskellinline{import Mod hiding (f1, t)
}\\
143 \multicolumn{2}{c
}{Basic types
}\\
145 \cleaninline{42 :: Int
} &
\haskellinline{42 :: Int
}\\
146 \cleaninline{True :: Bool
} &
\haskellinline{True :: Bool
}\\
147 \cleaninline{toInteger
42 :: Integer
} &
\haskellinline{42 :: Integer
}\\
148 \cleaninline{38.0 :: Real
} &
\haskellinline{38.0 :: Float -- or Double
}\\
149 \cleaninline{\"Hello\" +++ \"World\" :: String
}\footnote{Strings are unboxed character arrays.
}
150 &
\haskellinline{\"Hello\" ++ \"World\" :: String
}\footnote{Strings are lists of characters by default but may be overloaded as well if
\GHCmod{OverloadedStrings
} is enabled.
}\\
151 \cleaninline{['Hello'
] ::
[Char
]} &
\haskellinline{\"Hello\" :: String
}\\
152 \cleaninline{?t
} &
\haskellinline{Maybe t
}\\
153 \cleaninline{(?None, ?Just e)
} &
\haskellinline{(Nothing, Just e)
}\\
156 \multicolumn{2}{c
}{Type definitions
}\\
158 \cleaninline{:: T a0 ... :== t
} &
\haskellinline{type T a0 ... = t
}\\
159 \cleaninline{:: T a0 ...
} &
\haskellinline{data T a0 ...
}\\
160 \quad\cleaninline{= C1 f0 ... fn \| ... \| Cn f0 ... fn
} &
\quad\haskellinline{= C1 f0 ... fn \| ... \| Cn f0 ... fn
}\\
161 \cleaninline{:: T a0 ...
} &
\haskellinline{data T a0 ...
}\\
162 \quad\cleaninline{= \
{ f0 :: t0, ..., fn :: tn \
} } &
\quad\haskellinline{= T \
{ f0 :: t0, ..., fn :: tn \
} }\\
163 \cleaninline{:: T a0 ... =: t
} &
\haskellinline{newtype T a0 ... = t
}\\
164 \cleaninline{:: T = E.t: Box t \& C t
} &
\haskellinline{data T = forall t.C t => Box t
}\requiresGHCmod{ExistentialQuantification
}\\
167 \multicolumn{2}{c
}{Function types
}\\
169 \cleaninline{f0 :: a0 a1 ... -> t
}
170 &
\haskellinline{f0 :: (c0 v0, c1 v1, c2 v2) =>
}\\
171 \quad\cleaninline{\| c0 v0 \& c1, c2 v1
}
172 &
\quad\haskellinline{a0 -> a1 ... -> t
}\\
173 \cleaninline{(+) infixl
6 :: Int Int -> Int
} &
\haskellinline{infixl
6 +
}\\
174 &
\haskellinline{(+) :: Int -> Int -> Int
}\\
175 \cleaninline{qid :: (A.a: a -> a) -> (Bool, Int)
}
176 &
\haskellinline{qid :: (forall a: a -> a) -> (Bool, Int)
}\requiresGHCmod{RankNTypes
}\\
177 \cleaninline{qid id = (id True, id
42)
} &
178 \haskellinline{qid id = (id True, id
42)
}\\
181 \multicolumn{2}{c
}{Type classes
}\\
183 \cleaninline{class f a :: t
} &
\haskellinline{class f a where f :: t
}\\
184 \cleaninline{class C a \| C0, ... , Cn a
}\footnote{In contrast to the
\gls{HASKELL
} variant, this does not require an instance.
} &
\haskellinline{class (C0 a, ..., Cn, a) => C a
}\\
185 \cleaninline{class C s ~m where ...
} &
\haskellinline{class C s m \| m -> s where ...
}\requiresGHCmod{MultiParamTypeClasses
}\\
186 \cleaninline{instance C t \| C0, ..., Cn a
} &
\haskellinline{instance (C0 a, ..., Cn a) => C t
}\\
187 \quad\cleaninline{where ...
} &
\quad\haskellinline{where ...
}\\
190 \multicolumn{2}{c
}{As pattern
}\\
192 \cleaninline{x=:p
} &
\haskellinline{x@p
}\\
195 \multicolumn{2}{c
}{Lists
}\\
197 \cleaninline{[1,
2,
3]} &
\haskellinline{[1,
2,
3]}\\
198 \cleaninline{[x:xs
]} &
\haskellinline{x:xs
}\\
199 \cleaninline{[e \\\\ e <- xs \| p e
]} &
\haskellinline{[e \| e <- xs, p e
]}\\
200 \cleaninline{[l \\\\ l <- xs, r <- ys
]} &
\haskellinline{[l \| l <- xs, r <- ys
]}\\
201 \cleaninline{[(l, r) \\\\ l <- xs \& r <- ys
]} &
\haskellinline{[(l, r) \| (l, r) <- zip xs ys
]}\\
202 & or
\haskellinline{[(l, r) \| l <- xs \| r <- ys
]}\requiresGHCmod{ParallelListComp
}\\
205 \multicolumn{2}{c
}{Lambda expressions
}\\
207 \cleaninline{\
\a0 a1 ...->e
} or
\cleaninline{\\....e
} or
\cleaninline{\\...=e
} &
\haskellinline{\
\a0 a1 ...->e
}\\
210 \multicolumn{2}{c
}{Case distinction
}\\
212 \cleaninline{if p e0 e1
} &
\haskellinline{if p then e0 else e1
}\\
213 \cleaninline{case e of p0 -> e0; ...
} &
\haskellinline{case e of p0 -> e0; ...
}\\
214 \quad or
\cleaninline{case e of p0 = e0; ...
}\\
215 \cleaninline{f p0 ... pn
} &
\haskellinline{f p0 ... pn
}\\
216 \quad\cleaninline{\| c = t
} &
\quad\haskellinline{\| c = t
}\\
217 \quad\cleaninline{\| otherwise = t
} or
\cleaninline{= t
} &
\quad\haskellinline{\| otherwise = t
}\\
221 \multicolumn{2}{c
}{Record expressions
}\\
223 \cleaninline{:: R = \
{ f :: t \
}} &
\haskellinline{data R = R \
{ f :: t \
}}\\
224 \cleaninline{r = \
{ f = e \
}} &
\haskellinline{r = R \
{ f = e \
}}\\
225 \cleaninline{r.f
} &
\haskellinline{f r
}\\
226 &
\quad or
\haskellinline{r.f
}\requiresGHCmod[Requires
\gls{GHC
} version
9.2.0 or higher
]{OverloadedRecordDot
}\\
227 \cleaninline{r!f
}\footnote{This operator allows for field selection from unique records.
} &
\haskellinline{(\
\v->(f v, v)) r
}\\
228 \cleaninline{\
{r \& f = e \
}} &
\haskellinline{r \
{ f = e \
}}\\
231 \multicolumn{2}{c
}{Record patterns
}\\
233 \cleaninline{:: R0 = \
{ f0 :: R1 Int \
}} &
\haskellinline{data R0 = R0 \
{ f0 :: R1 Int \
}}\\
234 \cleaninline{:: R1 t = \
{ f1 :: t \
}} &
\haskellinline{data R1 t = R1 \
{ f1 :: t \
}}\\
235 \cleaninline{g \
{ f0 \
} = e f0
} &
\haskellinline{g (R0 \
{f0=x\
}) = e x
}\\
236 & or
\haskellinline{g (R0 \
{f0\
}) = e f0
}\requiresGHCmod{RecordPuns
}\\
237 \cleaninline{g \
{ f0 = \
{f1\
} \
} = e f1
} &
\haskellinline{g (R0 \
{f0=R1 \
{f1=x\
}\
}) = e x
}\\
240 \multicolumn{2}{c
}{Arrays
}\\
242 \cleaninline{:: A :== \
{t\
}} &
\haskellinline{type A = Array Int t
}\\
243 \cleaninline{a = \
{v0, ..., vn\
}} &
\haskellinline{a = array (
0, n+
1)
}\\
244 &
\quad\haskellinline{[(
0, v0), ..., (n, vn)
]}\\
245 \cleaninline{a = \
{e \\\\ p <-: a\
}} &
\haskellinline{a = array (
0, length a-
1)
}\\
246 &
\quad\haskellinline{[e \| (i, a) <- zip
[0..
] a
]}\\
247 \cleaninline{a.
[i
]} &
\haskellinline{a!i
}\\
248 \cleaninline{a!
[i
]}\footnote{This operator allows for field selection from unique arrays.
} &
\haskellinline{(\
\v->(v!i, v)) a
}\\
249 \cleaninline{\
{ a \&
[i
] = e\
}} &
\haskellinline{a//
[(i, e)
]}\\
252 \multicolumn{2}{c
}{Dynamics
}\\
254 \cleaninline{f :: a -> Dynamic \| TC a
} &
\haskellinline{f :: Typeable a => a -> Dynamic
}\\
255 \cleaninline{f e = dynamic e
} &
\haskellinline{f e = toDyn (e)
}\\
256 \cleaninline{g :: Dynamic -> t
} &
\haskellinline{g :: Dynamic -> t
}\\
257 \cleaninline{g (e :: t) = e0
} &
\haskellinline{g d = case fromDynamic d
}\\
258 \cleaninline{g e = e1
} &
\quad\haskellinline{Just e -> e0
}\\
259 &
\quad\haskellinline{Nothing -> e1
}\\
264 \input{subfilepostamble
}