declarative-js

My take on declarative programming in JavaScript.

This repository explains how to benefit from tiny functional programming modules to have your async JavaScript more declarative and readable. The final example code is available as example.js

The goal of the example code here will be defining one value that gives us all the user profiles provided by 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: "http://photos.foobar.com/19.jpg" }

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

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

At the first step, we need the list of users so we'll send a request to /users.json. It's a simple one, so we can 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(erroruserIds){
        
        userIds
        // => [3, 7, 19, 23, 27] 
        
})

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

Since we now have the list of users to get, we can get the user data;

var joinParams = require('join-params');
 
var user = joinParams(getJSON, "/users/{0}.json")

This time, we used join-params for getting the partial application of getJSON. join-params is a fork of new-partial that lets you join the parameters in a single, formatted parameter. See its docs for details.

Now we have the list of users and a singular user implementation. We'll use partial and map together to define the plural form of user;

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(errorallUsers){
        
        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(erroradmins){
        
        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.

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(getUser, '.posts', getPosts, '.photos', getPhotos);

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, getProfile);
var allProfiles  = comp(getUserIds, getProfiles);

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

allProfiles(function(errorprofiles){
        
    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. You can see the full example code in example.js on this repository.