{"_id":"straits","_rev":"1-42c5890f619f4696e7a2044fa6239bc8","name":"straits","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"straits","version":"1.0.0","description":"Straits is an implementation of traits for JavaScript. It defines some conventions about traits and provides libraries to aid their usage, definition and implementation.","main":"index.js","scripts":{"test":"babel-node test/index.js","prepare-watch":"babel --watch src --out-dir dist","prepare":"babel src --out-dir dist"},"keywords":["traits","straits"],"author":{"name":"peoro"},"license":"ISC","devDependencies":{"babel-cli":"^6.26.0","babel-plugin-transform-strict-mode":"^6.24.1","straits-babel":"^1.0.0"},"dependencies":{},"optionalDependencies":{},"babel":{"parserOpts":{"parser":"straits-babel"},"plugins":["straits-babel/plugin","transform-strict-mode"]},"gitHead":"526972a546d78edc29bf1792286aac9a509eb728","_id":"straits@1.0.0","_npmVersion":"6.4.1","_nodeVersion":"11.1.0","_npmUser":{"name":"peoro","email":"peoro.noob@gmail.com"},"dist":{"integrity":"sha512-PGcectNj37D0xPEkZbeIjV+qtYPNPcDexzSkJAH48XFJ3VD7FLXTWkV7+8yA658BWm4rICfPt1UgTzpukUO5eQ==","shasum":"fbfaff65a822f66e928c992e5fc3afe0292f0fa5","tarball":"https://registry.npmjs.org/straits/-/straits-1.0.0.tgz","fileCount":12,"unpackedSize":48086,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb7CnXCRA9TVsSAnZWagAA5IEP/jdXrrsgXS3VdlqbdWtY\nRhUEV8TrO9W4uQrI17QnOxZXLT96vh0LwL1FRp1QkxSIFectsY4aSJWJfT8m\nLlWDSpXsNQhL6HuzTnjbzDYewGBMjiYyMe6ElHC8CMRRDxGnjq0MErClB0Ru\nbABQysyShjE4LRq87OoPJKMgM9r+BBrHATAgCv0I7mVHo0sJ/QtKlIn2g6Uw\nvZy20cOauE/QJ5QxAipZKdmDYNbVEkr1JkOOGlpiEfDh1dA26m6H6jHRwh6t\nDDCVv1Lpijxl/59RObMue34MLK8jF53c+3NuJcsmLaLlxeLjqOXzWWvOlwdt\nLGihGouwFv0ADJKy2aADUnxZE5y1nAaG1eBqftGRwtM4HTL1QbQFb7dUGi+y\nqg9nc3YmsEkMYH6WlGcGqw5IYWUTnL+AUrBBHuSV5OfcHwhuf/WdPOH0Bmav\nF4dOfjY16MM58C2nNtOcjf0jYYRog3QHq1vn7TINFY7ooScMI5HP1E9IwyGS\naHCFizoH5yP8i70oQOUqfMUYDSdcztMYfZRQg68upake9N6XjM7wBap5CrXB\nr+BgaAIvlQQWZ9GxxykwjPjZXt6W1AC1myyaSiTVcAzVhHQ1kHFvMMhk3jf/\nVoG7mUne5GRMV3hyvpBQLxaDPBd2+JmA/Uy6E3iQTJlJ1caHUH2K16YFdpU/\n07i4\r\n=XZTs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGuJZ72SdCLI+9k9g41NdBB6dnSzm3ENfRW508tDH//ZAiEAstEQ+6r0u/1N8RDRYHDRi9iVOJMaNSGhMEM6os6G+PQ="}]},"maintainers":[{"name":"peoro","email":"peoro.noob@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/straits_1.0.0_1542203862505_0.34689947089419904"},"_hasShrinkwrap":false}},"time":{"created":"2018-11-14T13:57:42.505Z","1.0.0":"2018-11-14T13:57:42.700Z","modified":"2022-05-18T19:26:14.165Z"},"maintainers":[{"name":"peoro","email":"peoro.noob@gmail.com"}],"description":"Straits is an implementation of traits for JavaScript. It defines some conventions about traits and provides libraries to aid their usage, definition and implementation.","keywords":["traits","straits"],"author":{"name":"peoro"},"license":"ISC","readme":"\n# Straits\nStraits is an implementation of traits for JavaScript. It defines some conventions about traits and provides libraries to aid their usage, definition and implementation.\n\nTraits are a way to implement polymorphism: to extend objects and types with extra properties and behavior.\n\n  * [Quick example](#quick-example)\n  * [What are traits?](#what-are-traits)\n  * [How to use traits?](#how-to-use-traits)\n    + [Straits syntax](#straits-syntax)\n    + [Traits as member symbols](#traits-as-member-symbols)\n    + [Traits as free functions](#traits-as-free-functions)\n  * [Full example: `clone`](#full-example-clone)\n  * [Straits vs ...](#straits-vs-)\n    + [Classical properties](#classical-properties)\n    + [Free functions](#free-functions)\n  * [FAQ](#faq)\n    + [How does straits scoping work?](#how-does-straits-scoping-work)\n  * [Common errors](#common-errors)\n    + [SyntaxError: Unexpected identifier; SyntaxError: Unexpected token *](#syntaxerror-unexpected-identifier-syntaxerror-unexpected-token-)\n    + [No trait set is providing symbol x.](#no-trait-set-is-providing-symbol-x)\n    + [Symbol x offered by multiple trait sets.](#symbol-x-offered-by-multiple-trait-sets)\n  * [Status of straits](#status-of-straits)\n    + [Symbol versioning](#symbol-versioning)\n    + [Trait set extensibility](#trait-set-extensibility)\n    + [use trait t from traitSet](#use-trait-t-from-traitset)\n  * [Who uses straits?](#who-uses-straits)\n\n## Quick example\nImagine that you want to define your own serialization function. It needs to work with primitive types (e.g. `Number`, `String`, `Boolean`, ...) and standard ones (`Object`, `Array`, `Map`...) as well as with custom types defined by you or other libraries. Each type might need to specialize the serialization function in a different way.\n\nWhat's the correct way to do it?\nOur answer is traits!\n\nHere the code, using the [straits syntax](#straits-syntax):\n```javascript\n// defining the trait set:\n//const serializationTraitSet = { serialize: Symbol('serialize') };\n// a preferred way to define a trait set::\nimport {TraitSet} from 'straits';\nconst serializationTraitSet = new TraitSet('serialize');\n\n// telling the .* operator where to look for traits\nuse traits * from serializationTraitSet;\n\n// implementing the `serialize` trait for standard types\nObject.prototype.*serialize = function() { ... };\nArray .prototype.*serialize = function() { ... };\nNumber.prototype.*serialize = function() { ... };\n\n// implementing the `serialize` trait for custom types\nMyCustomType.prototype.*serialize = function() { ... };\n...\n\n// using the `serialize` trait on an object\n...\nobject.*serialize();\n```\n\nTraits offer many advantages above other possible approaches:\n - Traits make it trivial to specialize behavior in a truly polymorphic way.\n - Traits never break, disturb or collide with existing code. They're only relevant to the pieces of code where they're explicitly used.\n - Traits are fast. They exploit the JavaScript prototype chain without overhead.\n - When using the [straits syntax](#straits-syntax), traits aren't bothered by variables in the scope, and avoid polluting the latter.\n\n...Even the JavaScript standard, from ECMAScript 2015, is using them.\n\n## What are traits?\n**Traits** are a way to implement polymorphism: to extend objects and types with extra properties and behavior. If you want to learn more about traits, give a read to the [Wikipedia article](https://en.wikipedia.org/wiki/Trait_(computer_programming)).\n\nTraits are implemented using [**`symbol`**](https://developer.mozilla.org/en-US/docs/Glossary/symbol): a JavaScript primitive type, introduced in ECMAScript 2015 for purposes such as this one. It's a special type that can be used as key for object properties. Two different `symbol`s are always different: symbol properties cannot collide. `for(...in...)` loops also ignore `symbol` properties, and if `symbol`s are defined as non-enumerable (like the `.*` operator does), they're completely invisible (the only way to deal with such `symbol`s is by having an instance of it, or by using [`Object.getOwnPropertySymbols()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertySymbols)).\n\nAnother important building block to master traits (and JavaScript itself) is the [**prototype**-chain](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain). Each object in JavaScript has one prototype (which is a regular object as well). When an object's property is accessed, the property is looked for in the object itself; if it's not found it's searched in the object's prototype; if it's not yet found it's searched in the prototype's prototype, and so on recursively. `symbol`s can be defined on an object and/or on their prototype, just like regular properties.\n\nThe ECMAScript 2015 standard defines some standard `symbol`s and uses them as traits. Straits and the [straits syntax](#straits-syntax) is compatible with them. The standard uses names such as \"protocols\" to refer to them and an example is [`Symbol.iterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator) (see [iteration protocols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols)). When using the straits syntax, you can `use traits * from Symbol;` and then use `[].*iterator` and whatnot. Straits aims to introduce traits as a native feature of the language.\n\n## How to use traits?\nStraits encourages defining traits in a *trait set*: an object whose keys are regular strings and their values are `symbol`s. This is also what the global object [`Symbol`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol) does.\n\nIt's possible to use traits using three different styles:\n\n  - Using the [straits syntax](#straits-syntax), in the process of getting proposed for the next JavaScript standards. Currently available through a babel plugin: [`straits-babel`](https://github.com/peoro/straits-babel/).\n  - Using traits as [member symbols](#traits-as-member-symbols).\n  - Using traits as [free functions](#traits-as-free-functions).\n\nThe `straits.utils` module can help defining and using traits and trait sets in all of these fashions.\n\n### Straits syntax\nThe simplest and most performant way to use traits is by using a new syntax: the `.*` operator and `use traits` statement.\n\nFor more details, refer to the [straits-babel](https://github.com/peoro/straits-babel/) documentation: a babel plugin implementing the straits syntax.\n\nAssuming that `myTraitSet` provides a `myTrait` trait, the following piece of code...\n```javascript\nuse traits * from myTraitSet;\nobject.*myTrait\n```\n...would be roughly equivalent to:\n```javascript\nobject[ myTraitSet.myTrait ]\n```\n\n`.*` can also be used to assign traits, in which case the trait (i.e. `symbol` property) will be not-enumerable.\n\nThe `.*` operator only looks for the identifier to its right in the trait sets specified by the `use traits` statements. Variables available in the current scope won't interfere with the `.*` operator.\n\nThis syntax makes the code easier to write, read and understand. It avoids collisions between traits and scope variables and makes it easier and painless to import a large number of traits from a library.\n\nThe following is a real usage example:\n```javascript\nimport * as scontainers from 'scontainers';\nuse traits * from scontainers;\n\nconst array = [[1],7,[-2,5],2,7,[],4];\n\nconst result = array\n\t.*flatten()           // [1, 7, -2, 5, 2, 7, 4]\n\t.*reverse()           // [4, 7, 2, 5, -2, 7, 1]\n\t.*filter( item=>item%2 !== 0 )  // [7, 5, 7, 1]\n\t.*map( item=>item**2 )       // [49, 25, 49, 1]\n\t.*sum();\n// result: 124\n```\n\n### Traits as member symbols\nIt's possible to use traits directly, as they're just `symbol`s. Code written this way will offer the same performance as the `.*` syntax.\n\nBe 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.\n\nThe latest example from above could look like this:\n```javascript\nimport * as scontainers from 'scontainers';\n\nconst array = [[1],7,[-2,5],2,7,[],4];\n\nconst result = array\n\t[scontainers.flatten]()           // [1, 7, -2, 5, 2, 7, 4]\n\t[scontainers.reverse]()           // [4, 7, 2, 5, -2, 7, 1]\n\t[scontainers.filter]( item=>item%2 !== 0 )  // [7, 5, 7, 1]\n\t[scontainers.map]( item=>item**2 )       // [49, 25, 49, 1]\n\t[scontainers.sum]();\n// result: 124\n```\n\n### Traits as free functions\nIt's possible to create free functions that wrap traits.\n\nThis could introduce a small overhead, as the free function is an indirection, but it has the advantage of working with `null` and `undefined`.\n\nThe `straits.utils` module offers functions to generate free functions from traits. Trait sets defined with `starits-utils` (i.e. with `TraitsSet` as prototype) already offer a `freeFunction` property to obtain a set of free functions from a trait set.\n\nHere is once again the above example written using this approach:\n```javascript\nimport * as scontainers from 'scontainers';\nconst _ = scontainers.freeFunctions;\n// equivalent to:\n//import * as straits from 'straits';\n//import * as scontainers from 'scontainers';\n//use traits * from straits.utils;\n//const _ = scontainers.*traitsToFreeFunctions();\n\nconst array = [[1],7,[-2,5],2,7,[],4];\n\n// use `_` like you would use underscore or lodash:\nconst result =\n\t_.sum(\n\t\t_.map(\n\t\t\t_.filter(\n\t\t\t\t_.reverse(\n\t\t\t\t\t_.flatten(array)  // [1, 7, -2, 5, 2, 7, 4]\n\t\t\t\t),                    // [4, 7, 2, 5, -2, 7, 1]\n\t\t\t\titem=>item%2 !== 0\n\t\t\t),                                  // [7, 5, 7, 1]\n\t\t\titem=>item**2\n\t\t)                                    // [49, 25, 49, 1]\n\t);\n// result: 124\n```\n\n## Full example: `clone`\nImagine that you want to define a `clone` function.\n`clone` would need to be polymorphic: it needs to be implemented in different ways for `Object`s, `Array`s, `Number`s etc; some custom types might also need a custom serialization method.\n\nYou should define a `clone` trait, implement it for existing types and let other developers know that if needed they can implement such trait for the types they define:\n```javascript\n//const cloneTraits = {\n//\tclone: Symbol('clone')\n//};\n// or even better:\nimport * as straits from 'straits';\nconst cloneTraits = new straits.utils.TraitSet('clone');\n\nuse traits * from cloneTraits;\n\nObject.prototype.*clone = (obj)=>Object.create({}, obj);\nArray.prototype.*clone  = (arr)=>arr.slice()\nNumber.prototype.*clone = (num)=>num;\nString.prototype.*clone = (num)=>str;\n...\n\n\n// implementing the `clone` behavior for `null` and `undefined`\n// it will be used when the clone free function is called on something\ncloneTraits.clone.*implDefault( function(subject) {\n\tif( subject === undefined || subject === null ) {\n\t\treturn subject;\n\t}\n\n\tthrow new Error(`${subject.toString()} cannot be cloned.`);\n});\n\nexport default cloneTraits;\n```\n\nNote that a `clone` symbol is already defined in `straits.common`. That symbol should be used when implementing a similar semantics.\n\n## Straits vs ...\nTraits are arguably the best way to implement polymorphism, especially when one wishes to implement a new polymorphic behavior on existing types.\n\nOf course there are other ways to implement anything. Let's see some alternatives, taking the [`serialize` example](#quick-example) in consideration.\n\n### Classical properties\nThe simplest way to implement the `serialize` behavior could be by using a classical (string) property `\"serialize\"`:\n```javascript\nMyCustomType.prototype.serialize = function(){ ... };\n```\n\nThe big downside of this approach is that you should never modify existing types or objects (with non-`symbol` properties). That's very bad practice and it could quickly lead to undefined behaviors.\n\nThe problem is that existing code relies on properties (features; attributes; characteristics) of existing objects.\n\n1. What happens if two different libraries need to define their own serialization function? If they both end up choosing the same name (e.g. `serialize`), they would override each other's implementation, At least one of the libraries would call the wrong function and this will surely cause problems.\n2. Existing code could iterate on the properties of existing objects, and the unexpected encounter of a new, non-standard property could cause undefined behavior.\n3. Imagine that `Object.keys` and `Object.values` were members of `Object.prototype`. How could you use those functions on the object ``{ keys:[1,2,3], values:['a', 'b', 'c'] }``? The member functions would be overridden by the object's own properties.\n\nThat's the reason why no decent library does this. [Underscore](http://underscorejs.org/) and [lodash](https://lodash.com/), expose free functions. [jQuery](http://jquery.com/) encapsulates their data in custom objects rather than using `Array` etc.\n\n`symbol` properties can achieve exactly the same result while avoiding problems. They used to be more awkward to use, but the [straits syntax](#straits-syntax) is here to fix this issue.\n\n### Free functions\nOne could define `serialize` as a free function. The free function could enter different branches of code depending on the type of the object to serialize, but how could somebody specialize the behavior for a custom type introduced by a third library?\n\nThis is what libraries like [Underscore](http://underscorejs.org/) and [lodash](https://lodash.com/), do, but the result is that they only support very few types (i.e. `Object` and `Array`). They don't even support `Map` and `Set`; let alone custom types.\n\nIt could be possible to think of a way to register the specialization of a function in some data structure, but this could easily degrade performance, introduce memory leaks, fail to work with mixins etc. [`WeakSet`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet) (introduced in ECMAScript 2015 along with `symbol`) would be very useful for this task, but the result would most likely be inferior to using traits.\n\n## FAQ\n\n### How does straits scoping work?\n\nThe `.*` operator looks for its right identifier in a different scope from the regular variable's scope. The scope it uses is populated by the `use traits` statements.\n\nThe traits scopes have a visibility similar to regular scopes: they are valid only for the current block and all its nested ones.\nTraits used in inner scopes don't override those defined in outer scopes though, as shown in the following example:\n```javascript\nconst traits1 = { x:Symbol() };\nuse traits * from traits1;\n\n{\n\tconst traits2 = { x:Symbol() };\n\tuse traits * from traits2;\n\n\t// Error!\n\t// Symbol x offered by multiple trait sets.\n\t[].*x;\n}\n```\n\nThis is to avoid problems with API changes: imagine in fact that at the beginning of the development only `traits1` defined the `x` trait. Code everywhere was written relying on that specific trait.\nIf later during the development `traits2` adds an `x` trait, the existing code should not start using that one, as the semantics may differ.\n\nIdeally it should be possible to explicitly say in which trait set to look for a trait:\n```javascript\nconst traits1 = { x:Symbol() };\nuse traits x, * from traits1; // `x` is only looked for in `traits1`\n\n{\n\tconst traits2 = { x:Symbol() };\n\tuse traits * from traits2;\n\n\t[].*x; // same as `[][traits1.x]`\n}\n```\nBut this hasn't been implemented yet.\n\n## Common errors\n\n### SyntaxError: Unexpected identifier; SyntaxError: Unexpected token *\n\nThe `use traits` statement and `.*` operator aren't standard JavaScript. They're a proposed extension.\nCurrently, the only way to use them is transpiling them to valid JavaScript using [straits-babel](https://github.com/peoro/straits-babel/).\n\n### No trait set is providing symbol x.\n\nOne of the traits accessed with the `.*` operator is not provided by any trait set:\n\n```javascript\nconst traitSet = {};\nuse traits * from traitSet;\n\n[].*x;\n```\n\n### Symbol x offered by multiple trait sets.\n\nOne of the traits accessed with the `.*` operator is provided by 2 or more trait sets:\n\n```javascript\nconst traitSet1 = { x:Symbol() };\nconst traitSet2 = { x:Symbol() };\nuse traits * from traitSet1;\nuse traits * from traitSet2;\n\n[].*x;\n```\n\n## Status of straits\n\nStraits is still in its alpha stage. It already works and it's proving itself very useful, but a few important features are still missing.\n\n### Symbol versioning\n\nCurrently, if two different versions of a library that uses straits are used in the same project, all the symbols exposed by such library are duplicated.\n\nLet's consider a concrete example: `scontainers`.\nLet's say that the semantics of the `flatten` trait changes. The version of `scontainers` will be bumped, and new projects will start using the new version. Existing code might continue using the old one for a while.\nWhen both versions of `scontainers` are loaded in a project, two different full sets of traits will be created and coexist. The two versions of the same symbols (e.g. `map`, as well as `flatten`) will both be implemented for the standard types. The containers you defined, instead, will only implement the version of the traits your code is using. Some other modules used in the project (the ones that use the other version of `scontainers`) won't be able to use the container traits implemented on your objects.\n\nThe current behavior could be ok, but a different behavior might be preferable.\nThe traits whose semantics didn't change should use the same symbol even among two different versions of `scontainers`. Only the traits whose semantics changed should use different symbols (and thus different implementations).\nThis requires somehow versioning the symbols. It's something that a library (e.g. `scontainers`) could already do, but we believe that there should be a standard way and that `straits.utils` should offer an API to aid the effort.\n\n### Trait set extensibility\n\nIn most programming languages supporting traits (e.g. rust, haskell, go etc), one can automatically implement new traits for all the types that implement some other traits.\n\nThis cannot be easily done in JavaScript, as it's a dynamic language and virtually anything can implement traits at any point of the execution. Keeping a database of which objects are implementing which trait is not only very resource consuming, but would also result in memory leaks.\n\nThis feature would be very useful in JavaScript as well. Think of `scontainers`: a new trait `whileEach` could easily be implemented for every object that implements `forEach`. But how can we achieve that?\nAs above, we need to choose a convention to extend traits, and `straits.utils` should offer aiding functions.\n\n### use trait t from traitSet\n\nIt might happen that two different trait sets expose traits with the same name.\nFor instance `straits.math.log` is used to compute the logarithm function (i.e. `Math.log`), while `straits.console.log` is used to print values to the console (i.e. `console.log`).\nIf your code wants to both `use traits * from straits.math` and `use traits * from straits.console`, `.*log()` can not currently be used.\n\nAn advanced version of the `use traits` statement could help us here.\nImagine the following piece of code:\n```javascript\nimport * as straits from 'straits';\nuse traits * from straits.math;\nuse traits log, * from straits.console;\n7.*log();\n```\nIt means that all the traits are imported both from `straits.math` and from `straits.console`, but we are going to use the `log` symbol from `straits.console`.\n\n[`straits-babel`](https://github.com/peoro/straits-babel/) does not implement this syntax yet.\n\n## Who uses straits?\n\n - [`scontainers`](https://github.com/peoro/scontainers/), a container library that offers functional-style traits for containers. It achieves great performances and a polished semantics.\n - [`esast`](https://github.com/peoro/esast/), a library to manipulate JavaScript AST. Used by `scontainers` to generate efficient code on the fly.\n - `straits.core`, `straits.console` `straits.math` `straits.reflect`, modules exposing most of the free functions member of `Object`, `console`, `Map`, `Reflect` etc as traits.\n - `straits.common`, exposes a bunch of generic symbols that should be implemented by most types.\n - `Symbol`, the standard global object `Symbol` is compatible with straits.\n","readmeFilename":"README.md"}