-
Notifications
You must be signed in to change notification settings - Fork 2
/
MessageHandlers.tex
88 lines (67 loc) · 4.42 KB
/
MessageHandlers.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
\section{Message Handlers}
\label{message-handler}
Actors can store a set of callbacks---usually implemented as lambda expressions---using either \lstinline^behavior^ or \lstinline^message_handler^.
The former stores an optional timeout, while the latter is composable.
\subsection{Definition and Composition}
As the name implies, a \lstinline^behavior^ defines the response of an actor to messages it receives. The optional timeout allows an actor to dynamically change its behavior when not receiving message after a certain amount of time.
\begin{lstlisting}
message_handler x1{
[](int i) { /*...*/ },
[](double db) { /*...*/ },
[](int a, int b, int c) { /*...*/ }
};
\end{lstlisting}
In our first example, \lstinline^x1^ models a behavior accepting messages that consist of either exactly one \lstinline^int^, or one \lstinline^double^, or three \lstinline^int^ values.
Any other message is not matched and gets forwarded to the default handler \see{default-handler}.
\begin{lstlisting}
message_handler x2{
[](double db) { /*...*/ },
[](double db) { /* - unrachable - */ }
};
\end{lstlisting}
Our second example illustrates an important characteristic of the matching mechanism. Each message is matched against the callbacks in the order they are defined. The algorithm stops at the first match. Hence, the second callback in \lstinline^x2^ is unreachable.
\begin{lstlisting}
message_handler x3 = x1.or_else(x2);
message_handler x4 = x2.or_else(x1);
\end{lstlisting}
Message handlers can be combined using \lstinline^or_else^. This composition is not commutative, as our third examples illustrates. The resulting message handler will first try to handle a message using the left-hand operand and will fall back to the right-hand operand if the former did not match. Thus, \lstinline^x3^ behaves exactly like \lstinline^x1^. This is because the second callback in \lstinline^x1^ will consume any message with a single \lstinline^double^ and both callbacks in \lstinline^x2^ are thus unreachable. The handler \lstinline^x4^ will consume messages with a single \lstinline^double^ using the first callback in \lstinline^x2^, essentially overriding the second callback in \lstinline^x1^.
\clearpage
\subsection{Atoms}
\label{atom}
Defining message handlers in terms of callbacks is convenient, but requires a simple way to annotate messages with meta data.
Imagine an actor that provides a mathematical service for integers.
It receives two integers, performs a user-defined operation and returns the result.
Without additional context, the actor cannot decide whether it should multiply or add the integers.
Thus, the operation must be encoded into the message.
The Erlang programming language introduced an approach to use non-numerical
constants, so-called \textit{atoms}, which have an unambiguous, special-purpose type and do not have the runtime overhead of string constants.
Atoms in \lib are mapped to integer values at compile time.
This mapping is guaranteed to be collision-free and invertible, but limits atom literals to ten characters and prohibits special characters.
Legal characters are \lstinline^_0-9A-Za-z^ and the whitespace character.
Atoms are created using the \lstinline^constexpr^ function \lstinline^atom^, as the following example illustrates.
\begin{lstlisting}
atom_value a1 = atom("add");
atom_value a2 = atom("multiply");
\end{lstlisting}
\textbf{Warning}: The compiler cannot enforce the restrictions at compile time, except for a length check.
The assertion \lstinline^atom("!?") != atom("?!")^ is not true, because each invalid character translates to a whitespace character.
While the \lstinline^atom_value^ is computed at compile time, it is not uniquely typed and thus cannot be used in the signature of a callback.
To accomplish this, \lib offers compile-time \emph{atom constants}.
\begin{lstlisting}
using add_atom = atom_constant<atom("add")>;
using multiply_atom = atom_constant<atom("multiply")>;
\end{lstlisting}
Using these constants, we can now define message passing interfaces in a convenient way:
\begin{lstlisting}
behavior do_math{
[](add_atom, int a, int b) {
return a + b;
},
[](multiply_atom, int a, int b) {
return a * b;
}
};
// caller side: send(math_actor, add_atom::value, 1, 2)
\end{lstlisting}
Atom constants define a static member \lstinline^value^.
Please note that this static \lstinline^value^ member does \emph{not} have the type \lstinline^atom_value^, unlike \lstinline^std::integral_constant^ for example.