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 indist/
. Theprepare
npm script is automatically executed both bynpm install
andnpm 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:
- Using the straits syntax: the most performant and simplest way.
- Using traits as member symbols.
- Using traits as free functions.
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 symbol
s. 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.