Skip to content

Quickstart

Straits syntax setup

If you wish to use the straits syntax, create a new folder and run npm init @straits in there. This will lead you through the setup of a new Node project ready to use the custom syntax.

Once the project has been set up, run npm install to install the dependencies, then:

  • npm start, to run an example hello-world script.
  • npm test, to run the mocha test suite for the example hello-world.
  • npm run prepare to transpile your code using the straits syntax into standard JavaScript. The generated code will be placed in dist/. The prepare npm script is automatically executed both by npm install and npm publish: there's no risk to forget transpiling your code.

Now modify src/index.js to start implementing your own logic.

Using traits

It's possible to use traits using three different styles:

Let's take lodash-traits as an example.

Straits syntax

The simplest and most performant way to use traits is by using the .* operator and use traits statement.

For more details, refer to @straits/babel.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const lodashTraits = require('lodash-traits');

// enabling `.*` for `lodashTraits` symbols
use traits * from lodashTraits;

const result = [[1],7,[-2,5],2,7,[],4]
  .*flatten()           // [1, 7, -2, 5, 2, 7, 4]
  .*reverse()           // [4, 7, 2, 5, -2, 7, 1]
  .*filter( item=>item%2 !== 0 )  // [7, 5, 7, 1]
  .*map( item=>item**2 )       // [49, 25, 49, 1]
  .*sum();
// result: 124

Traits as member symbols

It's possible to use traits directly, as they're just symbols. Code written this way will offer the same performance as the .* syntax.

Be careful when assigning traits this way: assigning the trait directly (i.e. obj[trait] = value;) will result in the trait being enumerable. One should use Object.defineProperty(obj, trait, {value:value, configurable:true}); instead; that's what happens when obj.*trait = value; is evaluated.

The latest example from above could look like this:

1
2
3
4
5
6
7
8
9
const lodashTraits = require('lodash-traits');

const result = [[1],7,[-2,5],2,7,[],4];
  [lodashTraits.flatten]()           // [1, 7, -2, 5, 2, 7, 4]
  [lodashTraits.reverse]()           // [4, 7, 2, 5, -2, 7, 1]
  [lodashTraits.filter]( item=>item%2 !== 0 )  // [7, 5, 7, 1]
  [lodashTraits.map]( item=>item**2 )       // [49, 25, 49, 1]
  [lodashTraits.sum]();
// result: 124

Traits as free functions

It's possible to create free functions that wrap traits.

This could introduce a small overhead, as the free function is an indirection, but it has the advantage of working with null and undefined.

The @straits/utils module offers functions to generate free functions from traits. Trait sets defined with TraitSet from @straits/utils already have an .asFreeFunctions() method to obtain a set of free functions from a trait set.

Here is once again the above example written using this approach:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
const lodashTraits = require('lodash-traits');

// `_` is pretty much the same as the official `lodash`
const _ = scontainers.asFreeFunctions();

const result =
  _.sum(
    _.map(
      _.filter(
        _.reverse(
          _.flatten(
            [[1],7,[-2,5],2,7,[],4]
          )                 // [1, 7, -2, 5, 2, 7, 4]
        ),                  // [4, 7, 2, 5, -2, 7, 1]
        item=>item%2 !== 0
      ),                              // [7, 5, 7, 1]
      item=>item**2
    )                              // [49, 25, 49, 1]
  );
// result: 124

If you need to work with a trait set without an .asFreeFunctions() method (e.g. the global Well-known symbols), you can do:

1
2
3
4
5
6
7
const {TraitSet} = require('@straits/utils');

const fns = Symbol[ TraitSet.traitsToFreeFunctions ]();

const d = new Date();
console.log( d.*toPrimitive('number') ); // current timestamp
console.log( d.*toPrimitive('string') ); // current date as a string

Implementing traits

Traits can be implemented using:

  • the straits syntax:

    1
    2
    3
    const obj = {};
    use traits * from Symbol;
    obj.*toPrimitive = function(hint) { /*...*/ };
    

  • some @straits/utils functions:

    1
    2
    3
    const obj = {};
    use traits * from require('@straits/utils').TraitSet;
    Symbol.toPrimitive.*impl( obj, function(hint){ /*...*/ } );
    

  • Object.defineProperty:

    1
    2
    3
    4
    const obj = {};
    Object.defineProperty(obj, Symbol.toPrimitive, {
      configurable:true, value:function(){ /*...*/ }
    });
    

We discourage from assing member symbols directly, as they will end up being enumerable.

1
2
3
const obj = {};
// do NOT do this!
obj[ Symbol.toPrimitive ] = function(hint) { /*...*/ };

Defining new trait sets

We recommend to use @straits/utils to define new trait sets.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const {TraitSet} = require('@straits/utils');

const traitSet1 = new TraitSet({
  x: Symbol(),
  y: Symbol(),
});
// or
const traitSet2 = TraitSet.fromStrings([
  `x`,
  `y`,
]);

This way, the trait set will have a .asFreeFunctions() method.