Corey Montella - 21 Jul 2016
A clock example is pretty much the “Hello World” of reactive applications. It’s got all the right pieces: some event stream which changes over time, and a way to present those changes visually. Here is our version:
This clock is written in the Eve developer syntax, which is proposed in our first Request for Comments (RFC). In this post, I’ll go through the code and explain how it works.
Eve code is written as a series of blocks, each of which can be thought of as operating in two phases:
- Phase 1: Collect objects from the Eve DB
- Phase 2: Mutate objects in the Eve DB
This pattern repeats in each of the blocks. The clock program consists of two blocks: the first generically defines a clock hand, while the second defines the clock itself. Let’s focus on each block in turn.
Draw a clock hand
draw a clock hand // Select all the clock hands hand = [#clock-hand angle length] // Eve will maintain the following objects maintain // Calculate x1, y2, x2, y2 for each hand hand := [#line, x1: 50, y1: 50, // using angle and length x2: 50 + (length * sin(angle)), y2: 50 - (length * cos(angle))]
Blocks themselves have a structure. Any line indented at least once is treated as code, while any line with no intention is treated as a comment. This has the nice effect of being able to write documentation about the functionality of the program, with code blocks interspersed throughout. The rest of the block follows the collect -> mutate pattern.
hand = [#clock-hand angle length]
Objects are a set of attribute:value pairs enclosed in square brackets. They ask Eve to find all the entities that match the supplied attribute shape. The
hand object here is asking Eve to find every object tagged
#clock-hand that also has the attributes
length. Since we’ve just given the name of the attributes here, we can use them as variables later in the program e.g.
Now we enter the mutate phase of the query, indicated by the use of the
maintain fence. This fence says that we are finished reading from the Eve DB, and now we are ready to write to it. The use of the
maintain fence in particular says that as data changes, Eve will keep their dependant objects up-to-date. This is the first feature that enables the reactivity of the program (the clock hands change position as the time changes):
hand := [#line, x1: 50, y1: 50, x2: 50 + (length * sin(angle)), y2: 50 - (length * cos(angle))]
:= is the
set operator, one of three mutation opaertors (the other two being add
+= and remove
-=). The set operator sets the attributes of the target object to the supplied values, overwriting any previous values on the attribute. Here, the attributes being set are the
y2 coordinates of the clock hand.
That’s all this block does: it selects every
#clock-hand, and tags each one as a
#line, which Eve knows how to draw. In the next block, we define the clock as a clock face with
Draw a clock
draw a clock // Select the current time [#time hours minutes seconds] // Update the SVG as the time changes maintain // Add an SVG element to the root of the DOM [#svg viewBox: "0 0 100 100", width: "300px", children: // Add a clock face at (50,50) with radius 45. [#circle cx: 50, cy: 50, r: 45, fill: "#0B79CE"] // Add the hours hand [#clock-hand angle: 30 * hours, length: 30, stroke: "#023963"] // Add the minutes hand [#clock-hand angle: 6 * minutes, length: 40, stroke: "#023963"] // Add the seconds hand [#clock-hand angle: 6 * seconds, length: 40, stroke: "#ce0b46"]]
The second block (as with all blocks) follows the familiar collect -> mutate pattern. For this block’s collect phase, we are selecting the
#time object, with its attributes
seconds. This object is just like any other, but under the covers Eve keeps it up to date as the system clock changes. Then Eve updates every object that depends on time and so on.
maintain fence, we draw some SVG:
[#svg viewBox: "0 0 100 100", width: "300px", children: ... ]
Since no parent is specified, the SVG element is attached to the root of the DOM. Next, we draw some elements inside of the SVG box by adding them as children. We add the clock face (a blue circle at (50,50) with radius 45):
[#circle cx: 50, cy: 50, r: 45, fill: "#0B79CE"]
and three hands:
[#clock-hand angle: 30 * hours, length: 30, stroke: "#023963"]
This is where the current block ties back into the previous one. We’re adding an object with the tag
#clock-hand and attributes
length. This is exactly the pattern we were matching against in the first block! Thus, the loop is completed, and the clock hands are drawn as lines based on the current time as an angle.
Try it yourself!
You can download Eve and try the clock yourself! The quickest way is to use our docker container:
docker pull witheve/eve
But you can also download the Eve source and build it yourself:
git clone https://github.com/witheve/Eve.git. Instructions to build are available on the witheve/eve repository.