alloy-1.0.0: tutorial/Selective.lhs
The previous example applied the salary increase to all employees in the
company. Often, traversals need to be more selective, based on nodes further
up (i.e. closer to the root) in the tree. We will now consider how to
increase the salary of all employees except those that are anywhere in the
research department. We must bear in mind that departments may contain
departments:
\begin{code}
{-# LANGUAGE TypeOperators #-}
import CompanyDatatypes
import Data.Generics.Alloy
import Instances
increaseAllButResearch :: Float -> Company -> Company
increaseAllButResearch k = makeRecurse ops
where
ops :: Dept :- Salary :- BaseOp
ops = doDept :- incS k :- baseOp
doDept :: Dept -> Dept
doDept d@(D name _ _)
| name == "Research" = d
| otherwise = makeDescend ops d
incS :: Float -> Salary -> Salary
incS k (S s) = S (s * (1+k))
main = print $ increaseAllButResearch 0.1 genCom
\end{code}%$
There are several new concepts here. The main concept is the opset (short for
operations set). An opset is built using the \lstinline|:-| constructor in a
\textit{cons}-fashion, terminated by the \lstinline|baseOp| function (of
\lstinline|BaseOp| type). The type of an opset mirrors its construction,
showing that it is an opset on the two types \lstinline|Dept| and
\lstinline|Salary|. Usually the type of an opset can be inferred and thus it
is a matter of style whether to include the type.
An opset is used primarily with two functions: \lstinline|makeRecurse| and
\lstinline|makeDescend|. Broadly, \lstinline|makeRecurse| is used to
begin a traversal, and \lstinline|makeDescend| is used to continue it; \lstinline|makeRecurse| applies the
operations to all the largest types (the first ones encountered in a
depth-first search) it can find, potentially including the argument you have
given it -- in contrast, \lstinline|makeDescend| begins with the type's
children.
The \lstinline|increaseAllButResearch| function uses \lstinline|makeRecurse|
to begin the traversal of the company. However, \lstinline|doDept| must use
\lstinline|makeDescend| in order to operate on the children of the
\lstinline|Dept|. If \lstinline|doDept| had used \lstinline|makeRecurse|, an
infinite loop would have resulted from \lstinline|doDept| continually being
applied to the same department.
The function works by examining the department name. If the name is
\lstinline|"Research"|, the department is returned unaltered (as we do not
wish to alter any employees' salaries in research, even in sub-departments).
Otherwise, the traversal continues across the department, looking for further
sub-departments, and also salaries to increase as before.