log

taiar

Rust Programming Language - Usage

This is the eighth post of a serie where I’ll talk about some new programming languages with a nice future ahead. During this series I’ll explore deeper the Ceylon, Dart, Elixir, Rust and Swift 2 languages.

The plan is to make a post a week until December 15 (or maybe a little more) and spend 2 weeks exploring each one of them. In the first week I’ll explore the general aspects of the language and make some comparisons with other very known and established languages. In the post of the second week I’ll go deeper inside each one of the languages and explore the individual advantages on use them.

All the posts

  1. Ceylon Introduction
  2. Ceylon Usage
  3. Dart Introduction
  4. Dart Usage
  5. Elixir Introduction
  6. Elixir Usage
  7. Rust Introduction
  8. Rust Usage

Preface

There are hundred of programming languages out there. Which one should we use? Which help do we have to choose well? How do they compare to each other? This document is an attempt to provide some answers to these questions. Naturally, it would not be possible to provide complete answers: as I mentioned, there are too many programming languages. Nevertheless, we chose five languages with a potential to grow in importance in the coming years. These programming languages are Elixir, Rust, Dart, Swift and Ceylon. During this project, we shall be talking about each one of them. These discussions will be in breath, not in depth. Their goal is to provide the reader with the minimum of information necessary to compare them, and who knows, to lure one or other interested person in learning them in a greater level of details. In any case, we hope to contribute a bit to the popularization of these programming languages, which - likely - will be paramount to the development of computer science in the next ten years.

Rust Example

Rust was made to be a system’s programming language. There are lots of ways to integrate Rust programs into computer systems and other programming languages. In this usage example we’ll see how to write a system shared library in Rust and use it from other programs in other languages.

Our problem

This is the Monte Carlo algorithm for a Pi approximation

1 written in Python:

1
2
3
4
5
6
7
8
9
10
11
12
13
import random
import sys

def montecarlopi(n):
    m = 0.
    n = int(n)
    for i in range(0, n):
        x, y = random.random(), random.random()
        if ((x ** 2) + (y ** 2)) < 1:
            m = m + 1
    return 4 * m / n

print montecarlopi(sys.argv[1])

It computes a approximation for the Pi value, based in the given parameter. When bigger is the parameter, better is the approximation value. Let’s measure some executions:

1
2
3
4
5
6
$ time python pi.py 10
3.6

real    0m0.016s
user    0m0.015s
sys 0m0.000s

The programs is fast and the value is a very bad approximation. Let’s increase the precision:

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
$ time python pi.py 100000
3.14184

real    0m0.067s
user    0m0.058s
sys 0m0.008s

$ time python pi.py 1000000
3.140332

real    0m0.533s
user    0m0.504s
sys 0m0.028s

$ time python pi.py 10000000
3.1423776

real    0m5.192s
user    0m4.912s
sys 0m0.276s

$ time python pi.py 100000000
3.14162228

real    3m55.038s
user    0m52.011s
sys 0m6.175s

The program became very slow very fast. It’s a common approach to implement the slow algorithms of the systems in platforms that runs fast and integrate into the real workflow by calling this solution in other platform. So, let’s take a look on the implementation of this algorithm in Rust:

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
extern crate rand;

use std::env;
use std::str::FromStr;
use rand::distributions::{IndependentSample, Range};

fn montecarlopi(n: u32) -> f32 {
    let between = Range::new(-1f32, 1.);
    let mut rng = rand::thread_rng();

    let total = n;
    let mut in_circle = 0;

    for _ in 0..total {
        let a = between.ind_sample(&mut rng);
        let b = between.ind_sample(&mut rng);
        if a*a + b*b <= 1. {
            in_circle += 1;
        }
    }
    4. * (in_circle as f32) / (total as f32)
}

fn main() {
   let args: Vec<String> = env::args().collect();
   if args.len() > 1 {
     println!("{}", montecarlopi(FromStr::from_str(&args[1]).unwrap()));
   }
}

It Is the same algorithm at all. Let’s measure the execution of this program:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ time ./release/point 100000000
3.1413338

real    0m5.323s
user    0m1.911s
sys 0m3.410s

time ./release/point 1000000000
3.1415544

real    0m53.193s
user    0m19.046s
sys 0m34.107s

Awesome! The execution time is much better (even with more precision). Now we should make it callable from Python and other platforms.

A Rust library

Let’s start creating a new project with Cargo:

1
2
$ cargo new montepy
$ cd montepy

Now, edit the ./src/lib.rs file Cargo creates inserting our previous code with some changes I’ll explain later:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
extern crate rand;

use std::env;
use std::str::FromStr;
use rand::distributions::{IndependentSample, Range};

#[no_mangle]
pub extern fn montecarlopi(n: u32) -> f32 {
    let between = Range::new(-1f32, 1.);
    let mut rng = rand::thread_rng();

    let total = n;
    let mut in_circle = 0;

    for _ in 0..total {
        let a = between.ind_sample(&mut rng);
        let b = between.ind_sample(&mut rng);
        if a*a + b*b <= 1. {
            in_circle += 1;
        }
    }
    4. * (in_circle as f32) / (total as f32)
}

The not so odd modification is the pub extern annotations in the font of the montecarlopi function. The pub means that this function should be callable from outside of this module, and the extern says that it should be able to be called from C. Also the main method was removed because we don’t want this program to execute itself. The other modification was the # [no_mangle] annotation over the method. When you create a Rust library, it changes the name of the function in the compiled output. This attribute turns that behavior off 2.

Also we had to change the Cargo.toml file. It will look like this:

1
2
3
4
5
6
7
8
9
10
11
[package]
name = "montepy"
version = "0.1.0"
authors = ["André Taiar <taiar@dcc.ufmg.br>"]

[dependencies]
rand = "*"

[lib]
name = "montepi"
crate-type = ["dylib"]

The additions here where the dependencies clause, specifying that rand library is our dependence (Cargo will download and install it for us), the lib clause, specifying we are building a library and we want to compile our library into a standard dynamic library 3. Let’s build the library:

1
2
3
4
5
6
7
8
$ cargo build --release
    Updating registry `https://github.com/rust-lang/crates.io-index`
   Compiling winapi v0.2.5
   Compiling libc v0.2.2
   Compiling winapi-build v0.1.1
   Compiling advapi32-sys v0.1.2
   Compiling rand v0.3.12
   Compiling montepy v0.1.0 (file:///home/taiar/dev/mono1/rust/montepy)

Now in the ./target/release/ directory there is a file called libmontepi.so which is our compiled dynamic library. Now, we can create a python program that uses this library and compute our result very fast. Here is the code:

1
2
3
4
5
6
from ctypes import cdll

lib = cdll.LoadLibrary("target/release/libmontepi.so")
print lib.montecarlopi(100000000)

print("done!")

The execution result:

1
2
3
4
5
6
7
$ time python pi.py
22862944
done!

real    0m5.412s
user    0m2.061s
sys 0m3.343s

The program runs really fast but wait, there is a problem. The aproximation result is totally wrong.

The problem is that the integers and floats we use in our implementation of the Rust algorithm has different forms from the types of Python. To get around this, we must convert our numbers for types that are compatible with our system. Here is our new Python code:

1
2
3
4
5
6
7
8
9
10
from ctypes import cdll
import ctypes

lib = cdll.LoadLibrary("target/release/libmontepi.so")

montecarlopi = lib.montecarlopi
montecarlopi.restype = ctypes.c_float
print montecarlopi(100000000)

print("done!")
1
2
3
4
5
6
7
$ time python pi.py
3.14137887955
done!

real  0m5.400s
user  0m1.952s
sys 0m3.447s

and in Node.js (with the ffi package 4):

1
2
3
4
5
6
7
var ffi = require('ffi');

var pipi = ffi.Library('./target/release/libmontepi.so', {
    'montecarlopi': ['float', ['int']]
});

console.log(pipi.montecarlopi(100000000));
1
2
3
4
5
6
$ time nodejs pi.js
3.1415293216705322

real  0m5.466s
user  0m2.038s
sys 0m3.420s

and in C (the libmontepi.so file must be available in your system):

1
2
3
4
5
6
7
8
9
#include <stdint.h>
#include <stdio.h>

float montecarlopi(const int precision);

int main() {
  printf("%f\n", montecarlopi(100000000));
  return 0;
}
1
2
3
4
5
6
7
$ gcc -o pi pi.c -lmontepi
$ time ./pi
3.141783

real  0m5.367s
user  0m1.925s
sys 0m3.441s

References

Rust Programming Language - Introduction

This is the seventh post of a serie where I’ll talk about some new programming languages with a nice future ahead. During this series I’ll explore deeper the Ceylon, Dart, Elixir, Rust and Swift 2 languages.

The plan is to make a post a week until December 15 (or maybe a little more) and spend 2 weeks exploring each one of them. In the first week I’ll explore the general aspects of the language and make some comparisons with other very known and established languages. In the post of the second week I’ll go deeper inside each one of the languages and explore the individual advantages on use them.

All the posts

  1. Ceylon Introduction
  2. Ceylon Usage
  3. Dart Introduction
  4. Dart Usage
  5. Elixir Introduction
  6. Elixir Usage
  7. Rust Introduction
  8. Rust Usage

Preface

There are hundred of programming languages out there. Which one should we use? Which help do we have to choose well? How do they compare to each other? This document is an attempt to provide some answers to these questions. Naturally, it would not be possible to provide complete answers: as I mentioned, there are too many programming languages. Nevertheless, we chose five languages with a potential to grow in importance in the coming years. These programming languages are Elixir, Rust, Dart, Swift and Ceylon. During this project, we shall be talking about each one of them. These discussions will be in breath, not in depth. Their goal is to provide the reader with the minimum of information necessary to compare them, and who knows, to lure one or other interested person in learning them in a greater level of details. In any case, we hope to contribute a bit to the popularization of these programming languages, which - likely - will be paramount to the development of computer science in the next ten years.

Rust

How did it appear?

Rust 1 is a general-purpose, multi-paradigm, compiled, systems programming language. Nowadays, the Rust project is maintained and developed by Mozilla 2 Research but it starts as a personal project by Mozilla employee Graydon Hoare 3 4.

Graydon starts working on Rust in 2006. He was already a engineer at Mozilla working on compilers and tools for other languages. He started sketching Rust thinking in a lot of obvious good ideas, known and loved in other languages, that haven’t made it into widely-used systems languages, or are deployed in languages that have very poor (unsafe, concurrency-hostile) memory models.

After working long time as a hobby project, he decided to show one such prototype he’d been working on his spare time to his manager. Mozilla took an interest and set up a team to work on this language as a component of longer-term project to rebuild their browser stack around safer, more concurrent, easier technologies than C++. That larger project is called “servo”. Mozilla is funding Rust development because of that.

Mozilla began sponsoring the project in 2009 and announced it in 2010. The first Rust compiler, that was written in OCaml 5, was dropped and rewritten in a Rust version that successfully compiled itself in 2011. It is called rustc and uses LLVM 6 as it’s back end. The version 1.0 7 was released in 15 May 2015.

Why was it designed and implemented?

System’s programming languages became widely used on the last 50 years since people started using high-level languages to write operating systems. Nevertheless, there were always two problems constantly present in this niche:

  • It’s difficult to write secure code (due to the way that languages like C and C++ manages memory);
  • It’s hard (but essential) to write multi-threaded concurrent code due to the low support the languages of this niche offers to programmers to deal with the bugs that writing parallel programs would offer 8.

These are the problems that Rust was made to address. As it’s own creator says: Our target audience is “frustrated C++ developers” in which he includes himself. The performance of safe code (which is one of things that Rust aims) is expected to be slower than C++, however, the Rust program’s performance is comparable to C++ code that manually implements safer operations 9.

Rust is a compiled, multi-paradigm programming language. It supports pure-functional, actor-based concurrency, imperative-procedural and object-oriented programming styles.

How can we use it, e.g., install and run?

Linux and Mac OS

Rust has a simple installation proccess on Linux and Mac OS systems. Run the following line in the command terminal:

1
$ curl -sSf https://static.rust-lang.org/rustup.sh | sh

It will download the files, ask for sudo passwords and setup everything automatically.

Windows

There are binaries versions with installers for Windows users at the download page 10.

Testing the installation

Rust default toolset ships with a build tool called cargo 11. It is a tool to help manage the Rust projects. Cargo manages three things: building your code, downloading the dependencies your code needs, and building those dependencies.

To start a new project with cargo, run the command:

1
$ cargo new hello --bin

It will generate the dicrectory called hello with some files on it. On the directory root it creates a Cargo.toml file with some basic information about the project and a src directory with a main.rs file on it. This is the content of the main file:

1
2
3
fn main() {
    println!("Hello, world!");
}

To compile and run the project, in the project’s root directory, run the commands:

1
2
3
4
5
$ cargo build
   Compiling hello v0.1.0 (file:///home/taiar/dev/mono1/rust/hello)
$ cargo run
     Running `target/debug/hello`
Hello, world!

and the hello message just show up. The binary file is located at target/debug/hello and can be executed directly from the command line without the cargo tool too.

1
2
$ ./target/debug/hello
Hello, world!

Simple programs

Primitive types and mutability

Rust provides access to a wide variety of primitives:

  • signed integers: i8, i16, i32, i64 and isize (pointer size);
  • unsigned integers: u8, u16, u32, u64 and usize (pointer size);
  • floating point: f32, f64;
  • char Unicode scalar values like ‘a’, ‘α’ and ‘∞’ (4 bytes each);
  • bool either true or false;
  • arrays like [1, 2, 3];
  • tuples like (1, true).

Here are some usage:

1
2
3
4
5
6
7
8
9
10
11
12
13
fn main() {
    // Variables can be type annotated.
    let logical: bool = true;

    let a_float: f64 = 1.0;  // Regular annotation
    let an_integer   = 5i32; // Suffix annotation

    // Or a default will be used.
    let default_float   = 3.0; // `f64`
    let default_integer = 7;   // `i32`

    let mut mutable = 12; // Mutable `i32`.
}

Variables in Rust are immutable by default. The program:

1
2
3
4
fn main() {
    let truth = true;
    truth = false;
}

will result in a compile error:

1
error: re-assignment of immutable variable `truth` [E0384]

To be mutable, the variables must explicitly be annotated with mut:

1
2
3
4
fn main() {
    let mut lie = true;
    lie = false;
}

The version I’m using is Rust 1.5.0.

Object oriented programming

Rust uses structs to create more complex data types 12 13. For example, if we want to use two numbers to represent a point in the space:

1
2
3
4
5
6
7
8
9
10
11
12
13
struct Point {
    x: f32,
    y: f32,
}

fn main() {
    let mut point = Point { x: 0, y: 0 };

    point.x = 3;
    point.y = 4;

    println!("The point is at ({}, {})", point.x, point.y);
}

Rust also has the concept of Traits. A trait is a language feature that tells the Rust compiler about functionality a type must provide. If we want a method that tells the point distance to the origin:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
use std::f32;

struct Point {
    x: f32,
    y: f32,
}

impl Point {
    fn distanceFromO(&self) -> f32 {
        f32::sqrt(self.x * self.x + self.y * self.y)
    }
}

fn main() {
    let point = Point { x: 3f32, y: 4f32 };
    println!("The distance from the origin is ({})", point.distanceFromO(), );
}

The Trait implements the distanceFromO method for the Point struct.

Visibility in Rust is done around modules. Rust provides a powerful module system that can be used to hierarchically split code in logical units (modules), and manage visibility (public/private) between them. A module is a collection of items: functions, structs, traits, impl blocks, and even other modules 14.

References

Elixir Programming Language - Usage

This is the sixth post of a serie where I’ll talk about some new programming languages with a nice future ahead. During this series I’ll explore deeper the Ceylon, Dart, Elixir, Rust and Swift 2 languages.

The plan is to make a post a week until December 15 (or maybe a little more) and spend 2 weeks exploring each one of them. In the first week I’ll explore the general aspects of the language and make some comparisons with other very known and established languages. In the post of the second week I’ll go deeper inside each one of the languages and explore the individual advantages on use them.

All the posts

  1. Ceylon Introduction
  2. Ceylon Usage
  3. Dart Introduction
  4. Dart Usage
  5. Elixir Introduction
  6. Elixir Usage
  7. Rust Introduction
  8. Rust Usage

Preface

There are hundred of programming languages out there. Which one should we use? Which help do we have to choose well? How do they compare to each other? This document is an attempt to provide some answers to these questions. Naturally, it would not be possible to provide complete answers: as I mentioned, there are too many programming languages. Nevertheless, we chose five languages with a potential to grow in importance in the coming years. These programming languages are Elixir, Rust, Dart, Swift and Ceylon. During this project, we shall be talking about each one of them. These discussions will be in breath, not in depth. Their goal is to provide the reader with the minimum of information necessary to compare them, and who knows, to lure one or other interested person in learning them in a greater level of details. In any case, we hope to contribute a bit to the popularization of these programming languages, which - likely - will be paramount to the development of computer science in the next ten years.

Elixir example

On this usage example, we’ll use most of the concepts on the last article to build a more complex program that explores the concurrent features of Elixir. Starting with a Fibonacci function we’ll increment the program touching concepts that were not in the first article like pipes, processes, comunication between processes and tail call optimization.

Fibonacci

Let’s begin with a canonical starter recursive Fobonacci implementation with Elixir. Here is the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
defmodule Fib do

  def fib(0) do 0 end
  def fib(1) do 1 end
  def fib(n) do fib(n - 1) + fib(n - 2) end

end

IO.puts Fib.fib(0)
IO.puts Fib.fib(1)
IO.puts Fib.fib(2)
IO.puts Fib.fib(3)
IO.puts Fib.fib(4)
IO.puts Fib.fib(5)
IO.puts Fib.fib(6)
IO.puts Fib.fib(7)
IO.puts Fib.fib(8)

Nothing new here, just right to the point. Just write the problem’s definition and we got the right result:

1
2
3
4
5
6
7
8
9
10
$ elixir fib.ex
0
1
1
2
3
5
8
13
21

Many Fibonacci numbers

Let’s improve our last code to generate many numbers passing them as a list to the function. Here is the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
defmodule Fib do

  def run(list) do
    list
    |> Enum.map(&(fib(&1)))
    |> inspect
    |> IO.puts
  end

  def fib(0) do 0 end
  def fib(1) do 1 end
  def fib(n) do fib(n - 1) + fib(n - 2) end

end

Fib.run([0,1,2,3,4,5,6,7,8])

The |> symbol used in the snippet above is the pipe operator: it simply takes the output from the expression on its left side and passes it as the first argument to the function call on its right side. It’s similar to the Unix | operator. Its purpose is to highlight the flow of data being transformed by a series of functions. The run function without the pipe operator would be:

1
2
3
def run(list) do
  IO.puts(inspect(Enum.map(list, fn(x) -> fib(x) end )))
end

The Enum.map function calls the function on the second parameter for each element in the list passed as the first parameter returning a new list with the transformed elements. The inspect function returns a String witht the list “pretty printed” (written in a human readable format) and we print the list with the IO.puts function. The result of the execution, for whateaver run implementations is:

1
2
$ elixir fib.ex
[0, 1, 1, 2, 3, 5, 8, 13, 21]

Parallel Fibonacci numbers

In our last example, we run the function that calculates each Fibonacci’s number sequentially. The execution waits each of the numbers to be calculated before calculate the next number. Running this program for some bigger numbers would be slow. Let’s measure the time to calculate the Fibonacci numbers from 30 to 40:

1
2
3
4
5
6
$ time elixir fib.ex
[832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155]

real  1m2.186s
user  1m1.664s
sys 0m0.684s

Now I’ll rewrite the program to calculate these numbers using data parallelism. Our code will distribute the number calculation across different parallel computer cores possibly making the execution faster. Here is the code:

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
defmodule Fib do

  def run(list) do
    list
    |> Enum.with_index
    |> Enum.map fn(ni) -> spawn_run(self, ni) end
    receive_fibs(length(list), [])
  end

  defp receive_fibs(lns, result) do
    receive do
      fib ->
        result = [fib | result]
        if lns == 1 do
          IO.puts(print_fibs(result))
        else
          receive_fibs(lns - 1, result)
        end
    end
  end

  defp print_fibs(fibs) do
    fibs
    |> Enum.sort(fn({_, a}, {_, b}) -> a < b end)
    |> Enum.map(fn({f, _}) -> f end)
    |> inspect
  end

  def spawn_run(pid, ni) do
    spawn __MODULE__, :send_run, [pid, ni]
  end

  def send_run(pid, {n, i}) do
    send pid, {fib(n), i}
  end

  def fib(0) do 0 end
  def fib(1) do 1 end
  def fib(n) do fib(n - 1) + fib(n - 2) end
end

Fib.run([30,31,32,33,34,35,36,37,38,39,40])

Our already existent run function was modified. The Enum.with_index method returns the collection with each element wrapped in a tuple alongside its index. This way we can identify this number later to return them in the correct order. The Enum.map function that used to call the fib function now calls a spawn_run function. At the end of the run function it calls a receive_fibs function.

The spawn_run function just spawn off new processes1 of execution. The Elixir’s spawn function takes a function which it will execute in another process. It returns a PID (process identifier). The spawned process will execute the given function and exit after the function is done. The program uses the send and receive functions to comunicate between different proccesses. The send method receives a PID and a message and sends the message to the proccess with the referred PID. The receive function has one parameter that must be a function that will receive the potentially message sent.

So in our code, the spawn_run is called (for each one of the numbers we want the fibonacci number) and it calls a send_run function. The send_run function sends back a result of our already known fib functions. The receive function is used inside the receive_fibs function. receive_fibs works as a synchronization function that awaits the execution of all parallel fib calls, adding them to a list and returning this list when there is no more proccesses running.

When all the execution is over, the receive method calls the print_fibs function. This function orders the calculated number by its index (mentioned on the run function). This is necessary because the result of the parallel and asynchronous execution times are not deterministic and the order it returns the numbers is unknown. At last, the tuples with indexes are removed and just the result of the Fibonacci numbers are printed on the screen.

Now, this is the execution time of the parallel version of the program:

1
2
3
4
5
6
$ time elixir fib.ex
[832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155]

real  0m33.141s
user  1m22.564s
sys 0m1.208s

The real execution time of the parallel program is almost the half of the execution time for the sequential version of the code. This time would become even better if the machine that executes the program has a processor with a bigger number of execution cores.

Tail call optimization

Tail-call optimization is where you are able to avoid allocating a new stack frame for a function because the calling function will simply return the value that it gets from the called function.

The parallel portion of the implementations just remains the same. The modification is on the fib functions that now has a tail call, avoiding so many recursion calls. Here is the code:

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
defmodule Fib do

  def run(list) do
    list
    |> Enum.with_index
    |> Enum.map fn(ni) -> spawn_run(self, ni) end
    receive_fibs(length(list), [])
  end

  defp receive_fibs(lns, result) do
    receive do
      fib ->
        result = [fib | result]
        if lns == 1 do
          IO.puts(print_fibs(result))
        else
          receive_fibs(lns - 1, result)
        end
    end
  end

  defp print_fibs(fibs) do
    fibs
    |> Enum.sort(fn({_, a}, {_, b}) -> a < b end)
    |> Enum.map(fn({f, _}) -> f end)
    |> inspect
  end

  def spawn_run(pid, ni) do
    spawn __MODULE__, :send_run, [pid, ni]
  end

  def send_run(pid, {n, i}) do
    send pid, {fib(n), i}
  end

  def fib(n) do fib(n, 1, 0) end
  def fib(0, _, _) do 0 end
  def fib(1, a, b) do a + b end
  def fib(n, a, b) do fib(n - 1, b, a + b) end

end

Fib.run([30,31,32,33,34,35,36,37,38,39,40])

It might be more ilustrative if shown in a non-functional language, like Python:

1
2
3
4
5
def fib(i, current = 0, next = 1):
  if i == 0:
    return current
  else:
    return fib(i - 1, next, current + next)

We can see that now, the real execution time of our program is very smaller than our two last versions:

1
2
3
4
5
6
$ time elixir fib.ex
[832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155]

real  0m2.321s
user  0m2.152s
sys 0m0.320s

Quotes

References

Elixir Programming Language - Introduction

This is the fifth post of a serie where I’ll talk about some new programming languages with a nice future ahead. During this series I’ll explore deeper the Ceylon, Dart, Elixir, Rust and Swift 2 languages.

The plan is to make a post a week until December 15 (or maybe a little more) and spend 2 weeks exploring each one of them. In the first week I’ll explore the general aspects of the language and make some comparisons with other very known and established languages. In the post of the second week I’ll go deeper inside each one of the languages and explore the individual advantages on use them.

All the posts

  1. Ceylon Introduction
  2. Ceylon Usage
  3. Dart Introduction
  4. Dart Usage
  5. Elixir Introduction
  6. Elixir Usage
  7. Rust Introduction
  8. Rust Usage

Preface

There are hundred of programming languages out there. Which one should we use? Which help do we have to choose well? How do they compare to each other? This document is an attempt to provide some answers to these questions. Naturally, it would not be possible to provide complete answers: as I mentioned, there are too many programming languages. Nevertheless, we chose five languages with a potential to grow in importance in the coming years. These programming languages are Elixir, Rust, Dart, Swift and Ceylon. During this project, we shall be talking about each one of them. These discussions will be in breath, not in depth. Their goal is to provide the reader with the minimum of information necessary to compare them, and who knows, to lure one or other interested person in learning them in a greater level of details. In any case, we hope to contribute a bit to the popularization of these programming languages, which - likely - will be paramount to the development of computer science in the next ten years.

Elixir

How did it appear?

Elixir1 is a programming language designed to build scalable and maintainable systems. It first appeared in may 2012 after 1 year of underground development and at first it was a solo project from its creator and maintainer José Valim2. In the begining of 2012, Plataformatec3, a brazilian tech company where José Valim works (and is one of the owners) starts to sponsor and support the project and then the language got a real improvement.

Even before the first stable version was launched, Elixir attract many contributors and enthusiats. It counts 6 written books about programming in Elixir and other recipes4, 3 international conferences5 and 300+ open source contributors6. Besides having its own community, Elixir is also attracts members of the Ruby and Erlang communities too.

The version 1.0 of the language7 was released in September 10 in the year of 2014.

Why was it designed and implemented?

Back in 2010, José Valim was a very active member of the Ruby Language8 community. He worked on the core team of a major Ruby open source project called Ruby on Rails9 (a framework to build web applications). One of his contributions for this tool was to make the Ruby on Rails framework thread safe. At first, Ruby on Rails applications doesn’t scaled horizontally. At that time, there was no advantage on running a Rails application on multi-core machines. Making the framework thread safe means that the application written in Ruby on Rails when deployed in multi-core machines would run efficiently and use all processing power of that environment and in the right way (there will exist no problems with concurrency). Valim finds out that, on that time, there weren’t good tools on Ruby ecossistem to work with concurrency and he started do research about this topics trying to find in other languages and platforms the best ways to solve the problem they had in Rails.

During his explorations, Valim read the awesome 7 languages in 7 weeks10 where he meets the Erlang11 programming language. Erlang is a functional programming language and it has a virtual machine. It was developed by Ericsson12 and open sourced in 1998. It was designed for distributed and fault tolerant applications that runs in real time in a uninterrupted environment.

Very interested in the platform, he started to look deeper in the Erlang ecossystem writing programs and projects in Erlang. He loved the things the language has but misses things that he thought to be very important like good metaprogramming and polimorphysm. Despite these misses, the Erlang Virtual Machine was really awesome and Erlang was very good in concurrency.

At that time, there was only one language that had all the things Valim wishes to have in a language and it was Clojure13. Clojure is a dynamic language (no statyc typing), very extensible and with great focus on concurrency. Ruby was a dynamic and extensible language but was not good in concurrency scenarios like we saw. Go14 has a focus in concurrency but is static typed and not a very extensible language. Clojure has all he wants but is a JVM language (language that runs in the Java Virtual Machine) and he thinks that was a good idea to have other language option on this niche.

How can we use it, e.g., install and run?

The only prerequisite to run Elixir programs is Erlang 17.0 or later. So, before install the Elixir you must be sure to have Erlang installed.

Linux

There are easy install setups for all major Linux distributions. Most of them installs the Erlang Virtual Machine too, so yout don’t have do care with it. You can install on Ubuntu based distros with the commands:

1
2
3
$ wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb && sudo dpkg -i erlang-solutions_1.0_all.deb
$ sudo apt-get update
$ sudo apt-get install elixir

on Red Hat distros with:

1
$ yum install elixir

and on Arch Linux with the command:

1
$ pacman -S elixir

Mac OS

On Mac OS you can install using Homebrew:

1
2
$ brew update
$ brew install elixir

Windows

There are precompilled packages for Windows you can download and install on the Elixir download session at the website. You can also use the Chocolatey tool and install with:

1
cinst elixir

Testing the installation

After installing, let’s check if we installed right and we can run Elixir programs in our environment. Elixir code can be scripted and interpreted or compiled into bytecode that runs on the Erlang VM. Let’s checkt the interpreter.

Write and save the following code to the hello.exs file:

1
IO.puts "Hello, Elixir!"

and then, run the commands:

1
$ elixir hello.exs

and you’ll have the Hello, Elixir! sentence on your screen.

For the compilation to bytecodes, the Elixir program must be inside a module. Write the hello.ex file with the following contents:

1
2
3
4
5
defmodule Hello do
  def say do
    IO.puts "Hello, Elixir!"
  end
end

and compile with the command:

1
$ elixirc hello.ex

Check that the Elixir.Hello.beam file is created with bytecode in the same directory. Let’s check if the module works using the Elixir interactive environment. Run the program in the command line in this same directory:

1
2
3
4
5
$ iex
Erlang/OTP 18 [erts-7.1] [source] [smp:4:4] [async-threads:10] [kernel-poll:false]

Interactive Elixir (1.1.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>

In the interactive execution environment, type the code:

1
2
3
iex(1)> Hello.say()
Hello, Elixir!
:ok

and check that it loads the module and executes the function we defined.

Simple programs

Elixir is a functional language (the only functional-only language I learned on this work). Here are its basic types:

1
2
3
4
5
6
7
8
iex> 1          # integer
iex> 0x1F       # integer
iex> 1.0        # float
iex> true       # boolean
iex> :atom      # atom / symbol
iex> "elixir"   # string
iex> [1, 2, 3]  # list
iex> {1, 2, 3}  # tuple

There are some common features in functional languages that we’ll look on the next examples.

Anonymous functions

In the interactive execution environment, let’s see some things about anonymous functions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ iex
Erlang/OTP 18 [erts-7.1] [source] [smp:4:4] [async-threads:10] [kernel-poll:false]

Interactive Elixir (1.1.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> add = fn a, b -> a + b end
#Function<12.54118792/2 in :erl_eval.expr/5>
iex(2)> is_function(add)
true
iex(3)> add
#Function<12.54118792/2 in :erl_eval.expr/5>
iex(4)> add.(2, 4)
6
iex(5)> add_ten = fn a -> add.(a, 10) end
#Function<6.54118792/1 in :erl_eval.expr/5>
iex(6)> add_ten.(11)
21
iex(7)> x = add_ten.(0)
10
iex(8)> x
10
iex(9)> (fn -> x = 42 end).()
42
iex(10)> x
10

Functions are delimited by the keywords fn and end. They can be assigned to variables and passed as function parameters just as other types in the language. In the example, we assing a function that add two variables to a variable called add. To test it, we invoke the anonymous function with add. (we need that point after the variable name to call the anonymous functions) and then the parameters.

Anonymous functions are clojures and they can access variables that are in scope when the function is defined, like in the example when we create the add_ten variable. The variable assigned inside a function doesn’t affects the outer scope, so we check it on the last part of the execution.

Lists, tuples and immutability

Here are some operations with lists:

1
2
3
4
5
6
7
8
9
10
11
12
$ iex
Erlang/OTP 18 [erts-7.1] [source] [smp:4:4] [async-threads:10] [kernel-poll:false]

Interactive Elixir (1.1.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> music = [:a, :b, :c] ++ [1, 2, 3]
[:a, :b, :c, 1, 2, 3]
iex(2)> music -- [:c, 3]
[:a, :b, 1, 2]
iex(3)> hd(music)
:a
iex(4)> tl(music)
[:b, :c, 1, 2, 3]

There are the concatenation operator ++ and the subtractor operators that concatenates and subtract lists and there are the hd and tl functions that returns the head and the tail of the list.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ iex
Erlang/OTP 18 [erts-7.1] [source] [smp:4:4] [async-threads:10] [kernel-poll:false]

Interactive Elixir (1.1.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> tuple = {:ok, "hello"}
{:ok, "hello"}
iex(2)> put_elem(tuple, 1, "world")
{:ok, "world"}
iex(3)> elem(tuple, 0)
:ok
iex(4)> tuple_size(tuple)
2
iex(5)> elem(tuple, 1)
"hello"

We create a tuple using curly brackets. We can set a value for a determined tuple position, return a position and get a tuple size. Note in the last command that the second element of the variable tuple remains the same. The original tuple stored in the tuple variable was not modified because Elixir data types are immutable. By being immutable, Elixir code is easier to reason about as you never need to worry if a particular code is mutating your data structure in place.

Lists in Elixir are linked. This means that each element in a list holds its value and points to the following element until the end of the list is reached. Accessing the length of a list is a linear operation: we need to traverse the whole list in order to figure out its size. Updating a list is fast as long as we are prepending elements.

Tuples are stored contiguously in memory. This means getting the tuple size or accessing an element by index is fast. However, updating or adding elements to tuples is expensive because it requires copying the whole tuple in memory. Those performance characteristics dictate the usage of those data structures.

Pattern matching

Pattern matching allows to easily destructure data types such as tuples and lists. It is one of the foundations of recursion in Elixir. Here are some examples matching lists and tuples.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ iex
Erlang/OTP 18 [erts-7.1] [source] [smp:4:4] [async-threads:10] [kernel-poll:false]

Interactive Elixir (1.1.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> {a, b, c} = {:hello, "world", 42}
{:hello, "world", 42}
iex(2)> b
"world"
iex(3)> [a, b, c] = [1, 2, 3]
[1, 2, 3]
iex(4)> b
2
iex(5)> list = [1, 2, 3]
[1, 2, 3]
iex(6)> [0|list]
[0, 1, 2, 3]
iex(7)> [head | tail] = [1, 2, 3]
[1, 2, 3]
iex(8)> head
1
iex(9)> tail
[2, 3]

Modules

Elixir groups several functions into modules. In order to create our own modules we use the defmodule macro. We use the def macro to define functions in that module.

1
2
3
4
5
6
7
defmodule Math do
  def sum(a, b) do
    a + b
  end
end

Math.sum(1, 2)

Inside a module, we can define functions with def and private functions with defp. A function defined with def can be invoked from other modules while a private function can only be invoked locally.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
defmodule Concato do

  def concat(a, b) do
    do_concat(a, b)
  end

  defp do_concat([h|t], [a|b]) do
    [h|t] ++ [a|b]
  end

  defp do_concat(a, b) do
    a <> b
  end

end

IO.puts Concato.concat("Merry", "Christmas") #=> MerryChristmas
IO.inspect Concato.concat([:a, :b, :c], [1, 2, 3]) #=> [:a, :b, :c, 1, 2, 3]

The last example show the usage of a module with two private functions and a public function. The public function is used for interface with the module and it calls the possible 2 other functions using pattern matching. When the arguments are two lists, it calls a function that concatenate lists and when they are strings, the function that concatenates strings is called.

Recursion

Due to immutability, loops in Elixir (as in any functional programming language) are written differently from imperative languages. Functional languages rely on recursion: a function is called recursively until a condition is reached that stops the recursive action from continuing.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
defmodule Recursion do

  def print_multiple_times(msg, n) when n <= 1 do
    IO.puts msg
  end

  def print_multiple_times(msg, n) do
    IO.puts msg
    print_multiple_times(msg, n - 1)
  end

end

Recursion.print_multiple_times("Gol!", 7)

Let’s use recursion to make some math operations. The example above has three public functions: the first one return the sum of all numbers in the list and the second return the list with all elements multiplied by two.

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
defmodule Math do

  def sum_list([h|t]) do
    sum_list([h|t], 0)
  end

  defp sum_list([head|tail], accumulator) do
    sum_list(tail, head + accumulator)
  end

  defp sum_list([], accumulator) do
    accumulator
  end

  def double_each([head|tail]) do
    [head * 2|double_each(tail)]
  end

  def double_each([]) do
    []
  end

end

IO.puts Math.sum_list([2,3,5]) #=> 10
IO.inspect Math.double_each([2,3,5]) #=> [4, 6, 10]

Elixir has a Enum15 module that provides a set of algorithms that enumerate over collections. This module has a reduce and a map functions that we’ll use to rewrite our last example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
defmodule Mathenum do

  def sum_list([h|t]) do
    Enum.reduce([h|t], 0, fn(x, acc) -> x + acc end)
  end

  def double_each([h|t]) do
    Enum.map([h|t], fn(x) -> x * 2 end)
  end

end

IO.puts Mathenum.sum_list([2,3,5]) #=> 10
IO.inspect Mathenum.double_each([2,3,5]) #=> [4, 6, 10]

Quotes

References

Dart Programming Language - Usage

This is the fourth post of a serie where I’ll talk about some new programming languages with a nice future ahead. During this series I’ll explore deeper the Ceylon, Dart, Elixir, Rust and Swift 2 languages.

The plan is to make a post a week until December 15 (or maybe a little more) and spend 2 weeks exploring each one of them. In the first week I’ll explore the general aspects of the language and make some comparisons with other very known and established languages. In the post of the second week I’ll go deeper inside each one of the languages and explore the individual advantages on use them.

All the posts

  1. Ceylon Introduction
  2. Ceylon Usage
  3. Dart Introduction
  4. Dart Usage
  5. Elixir Introduction
  6. Elixir Usage
  7. Rust Introduction
  8. Rust Usage

Preface

There are hundred of programming languages out there. Which one should we use? Which help do we have to choose well? How do they compare to each other? This document is an attempt to provide some answers to these questions. Naturally, it would not be possible to provide complete answers: as I mentioned, there are too many programming languages. Nevertheless, we chose five languages with a potential to grow in importance in the coming years. These programming languages are Elixir, Rust, Dart, Swift and Ceylon. During this project, we shall be talking about each one of them. These discussions will be in breath, not in depth. Their goal is to provide the reader with the minimum of information necessary to compare them, and who knows, to lure one or other interested person in learning them in a greater level of details. In any case, we hope to contribute a bit to the popularization of these programming languages, which - likely - will be paramount to the development of computer science in the next ten years.

Dart example

To show some more details on the Dart language, I wrote a webapp example based on an existing Dart sample1. The application is a TODO list app (a list where you insert texts that represent tasks that you have do accomplish and you remove the items on the list that you already did). The application will work on the browser and will be necessary a minimal HTML2 implementation so we can interact with the program. After the code, I will make observations and further explanations. We will need two files:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>A Simple ToDo List Using Dart and HTML5</title>
    <link rel="stylesheet" href="todo.css">
  </head>
  <body>
    <input type="text" id="todo" name="todo" placeholder="What do you need to do?">
    <input type="submit" id="submit" value="Add Todo Item">
    <ul id="todo-items"></ul>
    <script src="out.js"></script>
  </body>
</html>

This is the basic HTML interface. It implements a HTML5 webpage with a stylesheet3 CSS file (which we’ll not care now, it’s only for a better page layout on this example), a text input to write our tasks, a button to add the task in the list and a unordered list where the tasks will appear after we add them in the list. It also include the JavaScript file named out.js which is the default name generated by the dart2js compiler we talked in the first article and is the result of the JavaScript compilation of our Dart program.

Now the code of the todo.dart file:

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
89
90
91
92
93
94
95
96
97
import 'dart:html';
import 'dart:indexed_db' as idb;
import 'dart:async';

class TodoList {
  static final String _TODOS_DB = "todo";
  static final String _TODOS_STORE = "todos";

  idb.Database _db;
  int _version = 2;
  InputElement _input;
  Element _todoItems;

  TodoList() {
    _todoItems = querySelector('#todo-items');
    _input = querySelector('#todo');
    querySelector('input#submit').onClick.listen((e) => _onAddTodo());
  }

  Future open() {
    return window.indexedDB.open(_TODOS_DB, version: _version,
        onUpgradeNeeded: _onUpgradeNeeded)
      .then(_onDbOpened)
      .catchError(_onError);
  }

  void _onError(e) {
    window.alert('Oh no! Something went wrong. See the console for details.');
    window.console.log('An error occurred: {$e}');
  }

  void _onDbOpened(idb.Database db) {
    _db = db;
    _getAllTodoItems();
  }

  void _onUpgradeNeeded(idb.VersionChangeEvent e) {
    idb.Database db = (e.target as idb.OpenDBRequest).result;
    if (!db.objectStoreNames.contains(_TODOS_STORE)) {
      db.createObjectStore(_TODOS_STORE, keyPath: 'timeStamp');
    }
  }

  void _onAddTodo() {
    var value = _input.value.trim();
    if (value.length > 0) {
      _addTodo(value);
    }
    _input.value = '';
  }

  Future _addTodo(String text) {
    var trans = _db.transaction(_TODOS_STORE, 'readwrite');
    var store = trans.objectStore(_TODOS_STORE);
    return store.put({
      'text': text,
      'timeStamp': new DateTime.now().millisecondsSinceEpoch.toString()
    }).then((_) => _getAllTodoItems())
    .catchError((e) => _onError);
  }

  void _deleteTodo(String id) {
    var trans = _db.transaction(_TODOS_STORE, 'readwrite');
    var store =  trans.objectStore(_TODOS_STORE);
    var request = store.delete(id);
    request.then((e) => _getAllTodoItems(), onError: _onError);
  }

  void _getAllTodoItems() {
    _todoItems.nodes.clear();

    var trans = _db.transaction(_TODOS_STORE, 'readwrite');
    var store = trans.objectStore(_TODOS_STORE);

    var request = store.openCursor(autoAdvance:true).listen((cursor) {
      _renderTodo(cursor.value);
    }, onError: _onError);
  }

  void _renderTodo(Map todoItem) {
    var textDisplay = new Element.tag('span');
    textDisplay.text = todoItem['text'];

    var deleteControl = new Element.tag('a');
    deleteControl.text = '[Delete]';
    deleteControl.onClick.listen((e) => _deleteTodo(todoItem['timeStamp']));

    var item = new Element.tag('li');
    item.nodes.add(textDisplay);
    item.nodes.add(deleteControl);
    _todoItems.nodes.add(item);
  }
}

void main() {
  new TodoList().open();
}

This file implements all the logic involved in our application. It deals with the addition and removal of the tasks on the list and all the interaction with the HTML5 structure. Those two files composes our example and implements a functional prototype. Now let’s compile and run the application. With the files in the same directory we just have to use the dart2js compiler:

1
2
$ dart2js todo.dart
Dart file (todo.dart) compiled to JavaScript: out.js

After that, if we open the index.html file in a modern web browser, the application will be working. Insert the tasks inside the text input and click the Add Todo Item button to insert a task in your TODO list. Click then on the [Delete] link in front of the task text to remove it.

TODO app working

Now I will talk about the usage of the Dart language in this small application.

Language structure

From the very basics, Dart is a class-based, single-inheritance, object-oriented language with C-style syntax.

The Dart language is also dynamically typed. You can write programs that have no type annotations whatsoever, and run them, much as you would in JavaScript. One of the Dart programming language’s most innovative features is the use of optional types. You may use type annotations in Dart. It will have some improvements to your program like documentation for humans, documentation for machines, early error detection (Dart provides a static checker that can warn you about potential problems) and sometimes, types can help improve performance when compiling to JavaScript.

The static checker acts a lot like in C. It warns you about potential problems at compile-time. Many of these warnings are related to types. The static checker does not produce errors - you can always compile and run your code, no matter what the checker says.

Let’s check out the example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Point {
  final num x, y;
  Point(this.x, this.y);
  Point operator +(Point other) {
    return new Point(x + other.x, y + other.y);
  }

  String toString() {
    return "x: $x, y: $y";
  }
}

main() {
  Point p1 = new Point(0, 0);
  Point p2 = new Point(10, 10);

  int n = p1 + p2;

  print(n);
}

The code above sends a warning in int n = p1 + p2; saying ``A value of type ‘Point’ cannot be assigned to a variable of type ‘int’``` if we run the program with the checked mode turned on. However, the program works nice with no checking at all.

When no types are provided, Dart avoid complaints by adding a default type called dynamic. We can use this type explicitly:

1
2
3
4
5
Map<String, dynamic> m = {
    'one': new Partridge(),
    'two': new TurtleDove(),
    /* ..., */
    'twelve': new Drummer()};

Dart has generics too. We can have this kind of list:

1
new List<String>();

but you will still be able to write something like

1
new List();

and use as a list of Strings. The previous example is just a shorthand for

1
new List<dynamic>();

The Dart constructor is a method of the class with the same name of the class. As said before, the protected methods starts with underscore and are not visible outside the package of the class.

Libraries again

Just like we saw in the language introduction, our program uses external libraries. The first one is the dart:html library3. This library manages HTML elements and other resources for web-based applications that need to interact with the browser and the DOM (Document Object Model). It includes DOM element types, CSS styling, local storage, media, speech, events, and more.

The second one is the dart:indexed_db library4. The IndexedDB is a web standard for an indexed database which works in the browser. By storing data on the client in an IndexedDB, a web app gets some advantages, such as faster performance and persistence. Many browsers supports the use of IndexedDB5.In our example, we a using this database to store the TODO tasks.

The third and last one is the dart:async library6 we saw in the early examples. In this program, we use async to deal with the events of send and get data from the HTML5 database (IndexedDB).

In our example, none of these libraries is external to the Dart SDK. So we don’t need to install them with the pub get command. They are all included with a Dart default installation.

Integration with modern browsers (HTML5, IndedexDB and JavaScript API)

As a language that aims to replace JavaScript on the next years, Dart must have a strong interoperability with HTML5 elements, browsers functionalities and the JavaScript existent libraries and apis. Filling the gaps of JavaScript in terms of functionality is a strategy aligned with Dart purpose to make web app development more pragmatic.

All of the HTML5 compatibility is implemented in the dart:html library. The first use of this library we can see in our example is with the function querySelector. This function is like a static method of the library. It finds the first descendant element of the HTMl document that matches the specified group of selectors (as parameters of the function) and returns an Element object (that describes a HTML object). The selectors are written in the form of CSS Selectors7. JavaScript don’t have a native function to match HTML elements by CSS selectors. This kind of “feature missing” in the JavaScript language brings lots of popularity for some JavaScript frameworks that aims to fill this kind of gaps in the language. The most famous of them is jQuery8 which is so present in JavaScript projects that sometimes is jumbled with the language itself.

The Element type in Dart9 is an abstract class, which all HTML elements extend. It implements all HTML browser behaviors like events as we can see in the snippet:

1
querySelector('input#submit').onClick.listen((e) => _onAddTodo());

The querySelector method matches a Input button Element field with the a ID attribute equals to “submit” and binds to it an event called onclick. When this button is clicked, the private method _onAddTodo is called.

The same patch would be written in vanilla JavaScript in this way:

1
2
3
4
document.getElementById("submit")
  .addEventListener("click", function(e){
    return _onAddTodo();
  }, false);

Or even in JavaScript with jQuery this way:

1
2
3
$("input#submit").on("click", function(e){
  return _onAddTodo();
});

The very next browser feature controlled by Dart in the example is the IndexedDB10 interaction. The Indexed Database API, or IndexedDB (formerly WebSimpleDB), is a recommended web browser standard interface for a local database of records holding simple values and hierarchical objects. IndexedDB was initially proposed by Oracle in 2009.

The code of the program that open the IndexedDB and return the results is:

1
2
3
4
5
6
7
Future open() {
  return window
    .indexedDB
    .open(_TODOS_DB, version: _version, onUpgradeNeeded: _onUpgradeNeeded)
    .then(_onDbOpened)
    .catchError(_onError);
}

It gets the window of the browser, open the database used by the application, sets the callback function for when the database is opened and ready to use and even sets a callback function to deal with errors, all in that lines.

The method that add things on the database is this one:

1
2
3
4
5
6
7
8
9
Future _addTodo(String text) {
  var trans = _db.transaction(_TODOS_STORE, 'readwrite');
  var store = trans.objectStore(_TODOS_STORE);
  return store.put({
    'text': text,
    'timeStamp': new DateTime.now().millisecondsSinceEpoch.toString()
  }).then((_) => _getAllTodoItems())
  .catchError((e) => _onError);
}

It opens a transaction11 with the database, create a object used to store things there and use its method called put passing the JSON object representing the TODO task to it. Same as before, the put method is chained with the methods that declares the callbacks for the function: one for the success and another one for errors.

Asynchronous again

Surely one of the keys concepts to write just about any Dart program is to understand the asynchronous model as well as the Future12 and Stream13 objects.

Very used in our example, a Future is used to represent a potential value, or error, that will be available at some time in the future. Receivers of a Future can register callbacks that handle the value or error once it is available. Those are the callbacks functions I said on the two methods in our previous topic. A interesting thing is that it applies to the methods that deals with Input and Output in the program, always heavy operations in any programming platform.

Quotes

References