A Declarative Clock in Eve

_This post has been updated. See the original here._

An analog clock is an obligatory example for reactive programming languages. It’s got all the right pieces: an event stream that changes over time, and a way to present those changes visually. Here is our version:

A Clock in Eve

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

Eve code is written as a series of blocks, each of which can be thought of as operating in two phases:

  • Phase 1: Match - select objects from the Eve DB by matching patterns
  • Phase 2: Action - change the Eve DB by either adding, setting, removing, or merging objects.

The clock program consists of two blocks: the first generically defines a clock hand, while the second defines the drawing of the clock. Let’s focus on each block in turn.

Draw a clock hand

First, we define how a clock hand is drawn. Although an analog clock has three hands, we can write code as if we’re drawing a single hand; Eve’s set semantics take care of drawing multiple hands, so no iterators like a for statement are necessary.

match

    // Select #clock-hands with angle and length attributes
    hand = [#clock-hand angle length]

    // Calculate coordinates for drawing a hand
    x2 = 50 + (length * sin[angle])
    y2 = 50 - (length * cos[angle])

// Bind tells Eve to update objects as values change
bind

    // Merge line coordinates into hand, tag it as a #line
    hand <- [#line, x1: 50, y1: 50, x2, y2]

Blocks start with the match phase, indicated by the match fence. Within the match, the programmer gathers all of the objects from the Eve DB needed to complete the block. Blocks only enter the action phase if all supplied objects in the match resolve against an object in the Eve DB.

In the first line of the match, we encounter our first object:

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 fit the supplied attribute shape. The hand object seen here is asking Eve to find every object tagged #clock-hand that also has the attributes angle and length. We use the angle and length attributes of this object to calculate two values, x1 and y2, which we will use as coordinates for the lines that draw the clock hands.

Now we enter the action phase of the query, indicated by the use of the bind fence (one of two fences applicable to the action phase). The bind fence says that we are finished reading from the Eve DB, and now we are ready to write to it. The use of the bind fence in particular says that as the objects in the match change, Eve will keep their dependant objects up-to-date.

hand <- [#line, x1: 50, y1: 50, x2, y2]

Here, we use the merge operator <-, one of four action operators. The merge operator merges one object with another. In this case, we are merging the object [#line, x1: 50, y1: 50, x2, y2] into the hand object. This means that every #clock-hand with angle and length attributes also has a #line tag, as well as x1, x2, y1, and y2 attributes.

That’s all this block does: it selects every [#clock-hand angle length], and tags each one as a #line, which Eve knows how to draw using x1, x2, y1, and y2. Let’s see how that’s done.

Draw a clock

In this block, we define the clock using a face drawn as a circle, and three #clock-hands drawn as lines.

match

    // Select the current time
    [#time hours minutes seconds]

// Update the SVG as the time changes
bind

    // 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 @hour-hand angle: 30 * hours, length: 30, stroke: "#023963"]

        // Add the minutes hand 
        [#clock-hand @minute-hand angle: 6 * minutes, length: 40, stroke: "#023963"]

        // Add the seconds hand 
        [#clock-hand @second-hand angle: 6 * seconds, length: 40, stroke: "#ce0b46"]]

This block (as with all blocks) follows the familiar match -> action pattern. For this block’s match, we select the current time, represented by the #time object; and its attributes hours, minutes, and seconds. This object is just like any other, but under the covers Eve keeps it up to date as the system clock changes.

Again we use the bind fence, because we want Eve to update the clock drawing as the time changes. Behind the bind fence, we add an SVG element:

[#svg viewBox: "0 0 100 100", width: "300px", children: ... ]

Since no parent is specified, the SVG element will be rendered as a child of the DOM root. Next, we add the clock elements as children of the SVG element. We add the clock face (a blue circle with a center at (50, 50) with radius 45):

[#circle cx: 50, cy: 50, r: 45, fill: "#0B79CE"] 

and three hands:

[#clock-hand @hour-hand angle: 30 * hours, length: 30, stroke: "#023963"]
[#clock-hand @minute-hand angle: 6 * minutes, length: 40, stroke: "#023963"]
[#clock-hand @second-hand angle: 6 * seconds, length: 40, stroke: "#ce0b46"]

Now we can see how this block ties back to the first one; the objects representing the hands of the clock are tagged #clock-hand and have angle and length attributes. 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 easiest way is to use our docker container:

docker pull witheve/eve

You can also download the Eve source and build it yourself (Linux and OSX only for now):

git clone https://github.com/witheve/Eve.git

Instructions to build are available on the witheve/eve repository.

Blog

Read up on new releases, technical musings and more.

Mailing List

Chat with the community and team about the future of Eve.

GitHub Issues

Let us know about any problems you run into using Eve.