[001.1] Introduction to Eve

Introducing Eve, a modern relational language for writing data-driven programs without the boilerplate.

Subscribe now

Introduction to Eve [07.04.2017]

Today we're going to look at getting started with Eve, described as a modern relational language for writing data-driven programs without the boilerplate. It's a promising language that I've been following for quite a while now, and it has some interesting and unique properties. Let's get started.

Project

We'll start by checking out the Eve website. It calls out a few high-level features of the language that are intriguing:

  • Instant feedback
  • Naturally documented
  • Designed for debugging
  • Concurrent and compositional
  • A single data model
  • Fits in your stack

Before we move on, let's install Eve.

git clone https://github.com/witheve/eve-starter.git
cd eve-starter
npm install

Then we'll start the program switcher, which will show us all of the Eve programs in this project and allow us to pick one:

npm start

Let's start by looking at the counter.eve program. Click on it and you can see a few counters being shown. What does the code look like?

vim programs/counter.eve

It's a markdown file with embedded Eve blocks, delimited by ~~~. Eve programs tend to be written in a literate programming style, which is a fantastic thing to see happen from the early days of a project.

# Counter

This program demonstrates:

- responding to events
- drawing elements on the screen
- reusing elements programatically
- extending an element

### Increment a Counter

Each button uses the referenced counter to increment itself. We need to think about two tags to accomplish this goal:

- `#html/event/click` is a record created by the HTML watcher any time a user clicks somewhere within an Eve app.
- `#ui/button` is a record we'll create a little later on to represent our button. The UI Watcher will helpfully draw it as a graphical button for us.

search [#html/event/click element: [#ui/button diff counter]]

commit counter.count := counter.count + diff ~~~

Build a Counter

For every #counter, we create a #ui/row that contains the elements that draw the counter. These rows are added automatically to the root of the DOM, but you could add them to a particular element instead adding them as children to the parent's record.

search
  counter = [#counter count]

bind
  [#ui/row counter sort: counter.sort style: [flex: "0 0 auto" width: "100px"] children:
    [#ui/button text: "-" diff: -1 counter]
    [#ui/text text: count counter style: [padding: "0 6px" flex: 1 text-align: "right"]]
    [#ui/button text: "+" diff: 1 counter]]

Add some counters programatically. To make n unique and independent counters, we need to add something to the committed counter that makes it unique. Since sort = {1, 2, 3, 4}, when we add it to the counter record we get 4 different counters. As an added bonus, we can use it to choose what order to display our counters in. We also add one #fancy counter, which is a standard counter with new styling.

search
  sort = math/range[start: 1 stop: 4]

commit
  [#counter sort count: 0]
  [#counter #fancy sort: 5 count: 0]

Extend the Counter

This block says: For every rows with a counter that is tagged fancy, add a style with a black background and pink text. Let's break it down. We search for all #ui/rows with a counter attribute. The counter is constrained to be only counters with a #fancy tag. Then we bind a new style to each.

search
  container = [#ui/row counter]
  counter.tag = "fancy"

bind
  container.style += [background: "black" color: "#FFD0E0"]

I'm not going to work through this right now, but I wanted to call it out as a
good introduction. Let's make our own program:

```sh
vim programs/helloworld.eve

Here, we'll create a markdown file that works through the quickstart tutorial:

# Hello World

Eve is based on the concept of a single data store that you can search in and
commit to. Let's start by committing a record:

commit [#greeting message: hello world] ~~~

Here we're committing a record with the tag greeting and a field message containing the string hello world. Tags are used to specify groups of related records.

We can search for records. Let's search for this record and display it on the screen:

search
  [#greeting message]

bind
  [#ui/text text: message]

Here we've created a new record. It's tagged with #ui/text. The Eve runtime will search for records of this type and display them on the screen. ```

That should be enough to see something in the browser. Let's open our program at http://localhost:8000/app/eveRoot/helloworld.eve.

We can see the message on the screen. Let's commit a new greeting record now and see what happens:

A search might return multiple records. We'll add a new record and see it
displayed:

commit [#greeting message: howdy world] ~~~ ```

We can see that now there are two #ui/text records committed, and both are displayed. Eve blocks will automatically react to their bound records being changed. We saw this happen by committing a new greeting after the block that searched for and rendered them. We can use this to create a simple clock.

Eve blocks react to changes in the data store automatically. Let's take
advantage of this to add a clock:

commit [#time #system/timer resolution: 1000] ~~~

Here we're committing a new record tagged with time. It uses #system/timer to create a timer that updates each second. Let's display it on the screen:

search
  [#time second]

bind
  [#ui/text text: second]

This time we're just binding a record. This is different than committing them, as this record won't persist until explicitly removed. ```

Next, we'll add a button and count how many times it is clicked:

We can commit a `ui/button` record to draw a button on the screen.

commit [#ui/button #increment text: +1] ~~~

Now this button will be an element of type increment containing the text +1. We will search for click events on this element and commit each click event to the store:

search
  event = [#html/event/click element: [#increment]]

commit
  [#clicked event]

Here, we're finding all clicks on our increment elements, and committing a record for each click.

Now our store contains a clicked record for each instance of us clicking on the button. We can count these with the gather/count function, then bind a text element that displays the result:

search
  how-many = gather/count[for: [#clicked]]

bind
  [#ui/text text: "The button has been clicked {{how-many}} times"]

Here we can see how to interpolate a value inside of a string. That's it for the
basic tutorial.

## Summary

In today's episode, we installed an Eve starter project and worked through the
introduction for Eve, explaining it as we went. I'm really excited by Eve and I
hope you'll play with it a bit. If you build something cool with it, please
reach out in the comments to let me know. See you soon!

## Resources

- [Eve website](http://witheve.com)
- [Eve quickstart docs](http://docs.witheve.com/v0.3/tutorials/quickstart/)
- [Eve quickstart tutorial](http://play.witheve.com/#/examples/quickstart.eve)
- [Eve 0.3 installation instructions](http://docs.witheve.com/v0.3/install/)
- [Gist of the completed helloworld.eve](https://gist.github.com/knewter/d404193a8f0b0a6622e5145d2ecea2f2)