Pico Engine welcoming your contributions

The node pico engine still needs work, and we invite you to participate in this open-source project (hosted on GitHub).

Extending the libraries

Besides the base language of KRL, supported by the krl-compiler and the associated run-time system, pico-engine-core, there are several libraries (sometimes called modules, because the functions they provide are invoked with the same syntax used for user-defined KRL modules). It is relatively easy to extend these so as to add functionality to KRL. This is done by providing JavaScript code to implement a new library function, as shown in the following example.

Motivation: round numbers

For example, while working on a pico-based project, we encountered the need for rounding a number to the nearest integer. A quick search of the documentation shows that the math library in the classic engine provided a round() function. However, as of this writing, that function had not yet been implemented.

Looking for a place to add new code

The pico-engine consists of several packages. The one which applies in this case is pico-engine-core and more specifically the file pico-engine-core/src/modules/math.js which contains the implementation of everything provided by the math library.

Adding code for a round() function

We added a new function with this code, aping existing functions within the library:

      round: mkKRLfn([
        'number',
        'precision'
      ], function (ctx, args) {
        if (!_.has(args, 'number')) {
          throw new Error('math:round needs a number')
        }

         return _.round(args.number, args.precision)
      })

A consultation of the lodash documentation on the Internet shows that it provides a round() function, which we can use, since the library already requires it. In general, NodeJS is a rich ecosystem of code with almost everything one can imagine already implemented and ready for our use. Since the lodash function we are going to use under the covers, as our JavaScript implementation of the KRL function, provides a precision argument, we'll allow that as well. Note to adjust the documentation accordingly.

Having changed the pico-engine-core, we restart our pico-engine.

Testing the new code

We created this ruleset as a test jig for our newly-added function:

ruleset round_test {
  meta {
    shares __testing, round
  }
  global {
    __testing = { "queries":
      [ { "name": "__testing" }
      , { "name": "round", "args": [ "number" ] }
      , { "name": "round", "args": [ "number", "precision" ] }
      ]
    }
    round = function(number,precision){
      pre = precision => precision.as("Number") | 0;
      num = number => number.as("Number") | null;
      math:round(num,pre)
    }
  }
}

We then created a pico, named "math", installed in it the round_test ruleset, and visited the Testing tab of the UI:

Using this testing jig, we were satisfied that we were getting good results, documented here:

round

round(<num>) - convert <num> to the nearest integer.

n = math:round(2.1);    // n = 2
m = math:round(2.7);    // m = 3

round can take an optional second argument, the number of decimal places, which extends to negative values in an intuitive way.

math:round(4066.123,2)   // 4066.12
math:round(4066.123,0)   // 4066
math:round(4066.123,-2)  // 4100

Contributing the new code

Satisfied that the code works correctly, we worked with git in our local clone of the GitHub repository:

$ git checkout -b selected-math-functions master
M	packages/pico-engine-core/src/modules/math.js
Switched to a new branch 'selected-math-functions'
$ git add packages/pico-engine-core/src/modules/math.js 
$ git commit -m "initial implementation"
[selected-math-functions 40f618e] initial implementation
 1 file changed, 53 insertions(+)
$ git push -u origin selected-math-functions

First (line 1), we create a new local branch off of master, named selected-math-functions. Next, we add the modified file to our local git repository (line 4), and commit the change (line 5). Finally, we share the new branch to the GitHub repository (line 8).

The next step is to visit GitHub and turn the new branch into a pull request, by clicking on the green button, "Compare & pull request":

This launches some automatic checks, which we followed. When they failed, we clicked on one of them to see the detailed messages and found that we needed to add a space after the comma between function arguments. Matthew suggested a way to fix code-style problems:

Automated tests

It is part of our coding culture at Pico Labs to include automated tests. So, our pull request would not be accepted without them. The tests are found in pico-engine-core/test/modules/math.test.js and we added these tests cases:

  t.is(
    await kmath.round({}, [0.46]), 0
  )
  t.is(
    await kmath.round({}, [-0.46]), -0
  )
  t.is(
    await kmath.round({}, [-0.46, 1]), -0.5
  )
  t.is(
    await kmath.round({}, [4066, 0]), 4066
  )
  t.is(
    await kmath.round({}, [4066, -1]), 4070
  )
  t.is(
    await kmath.round({}, [4066, -2]), 4100
  )
  t.is(
    await kmath.round({}, [4066, -3]), 4000
  )
  t.is(
    await kmath.round({}, [4066, -4]), 0
  )
  await terr('round', {}, [], 'Error: math:round needs a number')

To run the tests, from within the pico-engine-core folder, use 

$ npm run test

Requesting a review

On the GitHub web site, viewing our pull request, we select a reviewer (near the upper-right corner) and click on that person's name to request a review. Assuming everything is satisfactory, they will accept our change and merge it into the master branch.

While awaiting the review, you can return to the master branch using this command:

git checkout master

See the git cheatsheet for more information.

Cleaning up

Once your pull request has been reviewed and merged into the master branch, you need to do a little clean-up.

Delete the branch on GitHub, using the button in its UI, if this hasn't already been done by the reviewer.

You can delete your local git branch using this command:

git branch -d selected-math-functions

Copyright Picolabs | Licensed under Creative Commons.