declarative-js

My take on declarative programming in JavaScript.

npm install declarative-js
3 downloads in the last week
12 downloads in the last month

Declarative JavaScript

This repository explains how to benefit from tiny functional programming modules to have your async JavaScript more declarative and readable.

Hacker News Thread | Share on Twitter

Index

Install & Run the Code Example

The code example I'll be explaining is available as example.js on this repository. You can run it as follows;

$ git clone https://github.com/azer/declarative-js.git
$ cd declarative-js
$ npm install
$ node example

First Steps

The goal of the example code here will be defining one value that gives us all the user profiles, like the below piece of code;

allProfiles = andThen(userIds, profiles) // function composition of `userIds` and `profiles`

From an API with following end-points:

=> /users.json [3, 7, 19, 23, 27]
=> /users/7.json { id: 7, name: 'Smith', age: 21, posts: [3, 11, 12], photos: [19, 23, 39] }
=> /posts/11.json { id: 11, title: "Hello World", content: "lorem ipsum sit dolor amet" }
=> /photos/19.json { id: 19, path: "/photos/19.jpg" }

Fetching Data

What do we need first; a function to query the JSON API?

var getJSON = require('get-json');

At the first step, we need the full list of users, and will send a request to /users.json. It's a simple one, we can simply do partial application on getJSON:

var partial = require('new-partial');

var userIds = partial(getJSON, '/users.json');

The userIds above is a new function. Once you call it, it'll fetch /users.json for you;

userIds(function(error, userIds){

        userIds
        // => [3, 7, 19, 23, 27]

})

But we won't need to call it actually. Only the definition of it is needed for us.

Defining Async Values

The definition of an async value here is the partial application of any async function, just like the userIds we defined above.

As the next step, we'll be defining user to get user data from the API. This will be a partial application, too, except that we need to format the first parameter this time;

var joinParams = require('join-params');

var user = joinParams(getJSON, "/users/{0}.json")

join-params is a fork of new-partial that lets you join the parameters in a single, formatted parameter. See its docs for details.

Defining Async Lists

Defining a list of async value means creating a new partial application of map with the async value as first parameter. This abstraction will let us fetch a group of users at once;

var map = require('map');

var users = partial(map, user);

At this step, we have user ids and a collection that implements a group of users. All we need is to combine these two together, using a function composition library, comp:

var comp = require('comp');

var allUsers = comp(userIds, users);

allUsers(function(error, allUsers){

        allUses[0].name, allUsers[2].age
        // => Smith, 23

})

Another example, partial application of users ?

var adminIds = [3, 7, 9];
var admins = partial(users, adminIds);

admins(function(error, admins){

        admins[0].name, admins[1].age, admins[1].photos
        // => "Smith", 21, [7, 13, 37, 43]

})

Until this point, we're able to get all users with their data excluding posts and photos, since they require separate API calls.

Let's define posts and photos, as well.

var post   = joinParams(getJSON, "/posts/{0}.json"),
    posts  = partial(map, post),

    photo  = joinParams(getJSON, post),
    photos = partial(map, photo);

Now we can combine these together and define a value that has everything (posts, photos) about a user.

Combining Values Together

I'll call the new value profile. And we'll use a function composition library called andthen to combine user, posts and photos values:

var andThen = require('andthen');

var profile = andThen(user, '.posts', posts, '.photos', photos);

andthen is a fork of comp that allows you to bind new values to properties of previous values. The code above will fetch user and pass the posts property to getPosts, and replace the same property with what getPosts returns. Same as photos.

Let's define the plural define of it.

var profiles     = partial(map, profile);
var allProfiles  = comp(getUserIds, profiles);

Final Value: allProfiles

Now we have everything we need. Let's show the output:

allProfiles(function(error, profiles){

    if(error) throw error;

    profiles.forEach(function(profile){

            profile.name, profile.age
            // => Smith, 21

            profile.photos[0].path
            // =>  "http://photos.foobar.com/19.jpg"

            profile.posts[0].title
            // => Hello World

    })

})

That's all. Check out the example code for more experience. Thanks for reading!

See Also

npm loves you