{"id":13451794,"url":"https://github.com/adobe/ferrum","last_synced_at":"2025-03-23T19:32:44.933Z","repository":{"id":34943379,"uuid":"189205816","full_name":"adobe/ferrum","owner":"adobe","description":"Features from the rust language in javascript: Provides Traits/Type classes \u0026 a hashing infrastructure and an advanced library for working with sequences/iterators in js","archived":false,"fork":false,"pushed_at":"2025-03-22T00:57:30.000Z","size":3658,"stargazers_count":522,"open_issues_count":53,"forks_count":26,"subscribers_count":41,"default_branch":"master","last_synced_at":"2025-03-22T01:34:39.920Z","etag":null,"topics":["ferrum","functional","hashing","iterator-support","iterators","javascript","object-hash","sequences","trait"],"latest_commit_sha":null,"homepage":"https://www.ferrumjs.org","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/adobe.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-05-29T10:42:34.000Z","updated_at":"2025-03-14T14:17:21.000Z","dependencies_parsed_at":"2024-02-23T22:26:57.836Z","dependency_job_id":"a1bf99c1-77cb-4700-9a99-697ac5af23b9","html_url":"https://github.com/adobe/ferrum","commit_stats":{"total_commits":236,"total_committers":17,"mean_commits":"13.882352941176471","dds":0.5254237288135593,"last_synced_commit":"0fb4c691f3607b06753314394e590f98e53559e8"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adobe%2Fferrum","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adobe%2Fferrum/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adobe%2Fferrum/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adobe%2Fferrum/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adobe","download_url":"https://codeload.github.com/adobe/ferrum/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244894303,"owners_count":20527669,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ferrum","functional","hashing","iterator-support","iterators","javascript","object-hash","sequences","trait"],"created_at":"2024-07-31T07:01:02.775Z","updated_at":"2025-03-23T19:32:43.940Z","avatar_url":"https://github.com/adobe.png","language":"JavaScript","readme":"\u003ca name=\"ferrum\"\u003e\u003c/a\u003e\n# Ferrum\n\nFeatures from the Rust language in JavaScript: Provides [Traits](https://doc.rust-lang.org/rust-by-example/trait.html) \u0026 an advanced library for working with sequences/iterators in JS.\n\n[Github](https://github.com/adobe/ferrum)  \n[API Documentation](https://www.ferrumjs.org)\n\n\u003ca name=\"table-of-contents\"\u003e\u003c/a\u003e\n## Table of Contents\n\n- [Ferrum](#ferrum)\n  - [Table of Contents](#table-of-contents)\n  - [Status](#status)\n  - [Usage \u0026 Features](#usage--features)\n    - [Hashing](#hashing)\n    - [Testing of examples in Documentation](#doctest)\n    - [Sequence/Iterators](#sequenceiterators)\n      - [Objects as Sequences](#objects-as-sequences)\n      - [Reverse Currying](#reverse-currying)\n      - [Pipelining](#pipelining)\n      - [Lazy Evaluation](#lazy-evaluation)\n    - [Traits](#traits)\n    - [Operators as functions](#operators-as-functions)\n    - [Typing utilities](#typing-utilities)\n    - [Functional Utilities](#functional-utilities)\n  - [Change Log](#change-log)\n    - [1.9.1](#191)\n    - [1.9.0](#190)\n    - [1.8.0](#180)\n    - [1.7.0](#170)\n    - [1.6.0](#160)\n    - [1.5.0](#150)\n    - [1.4.0](#140)\n    - [1.3.0](#130)\n    - [1.2.0](#120)\n  - [Development](#development)\n    - [Build](#build)\n    - [Test](#test)\n    - [Lint](#lint)\n\n\u003ca name=\"status\"\u003e\u003c/a\u003e\n## Status\n\n[![CircleCI](https://img.shields.io/circleci/project/github/adobe/ferrum/master.svg)](https://circleci.com/gh/adobe/ferrum/tree/master)\n[![codecov](https://img.shields.io/codecov/c/github/adobe/ferrum.svg)](https://codecov.io/gh/adobe/ferrum)\n[![GitHub license](https://img.shields.io/github/license/adobe/ferrum.svg)](https://github.com/adobe/ferrum/blob/master/LICENSE.txt)\n[![GitHub issues](https://img.shields.io/github/issues/adobe/ferrum.svg)](https://github.com/adobe/ferrum/issues)\n[![LGTM Code Quality Grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/adobe/ferrum.svg?logo=lgtm\u0026logoWidth=18)](https://lgtm.com/projects/g/adobe/ferrum)\n[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)\n\n\u003ca name=\"usage-features\"\u003e\u003c/a\u003e\n## Usage \u0026 Features\n\n```bash,notest\n$ npm add ferrum\n```\n\u003ca name=\"hashing\"\u003e\u003c/a\u003e\n### Hashing \u0026 Hash Tables\n\nFerrum features an extensible, reliable infrastructure for object hashing\nincluding an implementation of\n[HashMap](https://www.ferrumjs.org/module-hashing-HashMap.html) and\n[HashSet](https://www.ferrumjs.org/module-hashing-HashSet.html).\n\nIt supports [user defined hash functions](https://www.ferrumjs.org/module-hashing-Hasher.html)\n(e.g. blake2 instead of xxhash).\nSupport for all of the standard types is provided out of the box and\n[support for user defined types](https://www.ferrumjs.org/module-hashing-Hashable.html)\nor third party types can be provided via the trait infrastructure.\n\nYou could even integrate the\n[object-hash](https://www.npmjs.com/package/object-hash) package to add support\nfor hashing arbitrary third party types! See \"Sophisticated hasher integrating\nobject hash\"  in the\n[hasher trait](https://www.ferrumjs.org/module-hashing-Hasher.html) documentation.\n\n```js\nconst assert = require('assert');\nconst { HashMap } = require('ferrum');\n\nconst m = new Map([[{}, 42], [7, \"seven\"]]);\nassert.strictEqual(m.get(7), \"seven\");\nassert.strictEqual(m.get({}), undefined); // Identity based lookup\n\nconst hm = new HashMap([[{}, 42], [7, \"seven\"]]);\nassert.strictEqual(hm.get(7), \"seven\");\nassert.strictEqual(hm.get({}), 42); // Content based lookup\n```\n\n\u003ca name=\"doctest\"\u003e\u003c/a\u003e\n### Testing of Examples\n\nHave you ever found out that some of the examples in your api documentation\nor readme contained bugs? You can now use the [Ferrum Doctest](https://github.com/adobe/ferrum.doctest)\ncompanion package to run your examples as part of your regular test harness!\n\n\u003ca name=\"sequence-iterators\"\u003e\u003c/a\u003e\n### Sequence/Iterators\n\n| Feature              | Ferrum | Underscore | Lodash | wu.js |\n| -------------------- | ------ | ---------- | ------ | ----- |\n| Objects as Sequences |    yes |         no |     no |    no |\n| Reverse Currying     |    yes |         no |     no |    no |\n| Lazy Evaluation      |    yes |         no |     no |   yes |\n| Pipelining           |    yes |         no |     no |    no |\n\nFerrum provides a library for transforming lists \u0026 iterables; it provides all the functions\nyou would expect like `map`, `filter`, `foldl` and many others. In this regard it is very similar\nto libraries like wu.js, lodash or underscore. Ferrum has been written to remedy some of the issues\nin these libraries.\n\n\u003ca name=\"objects-as-sequences\"\u003e\u003c/a\u003e\n#### Objects as Sequences\n\n`Ferrum/Sequence` has been designed with full iterator support in mind. Generally all functions\ncan take iterables/iterators and returns iterators.\n\n```js\nconst {map, assertSequenceEquals} = require('ferrum');\n\nconst a = map([1,2,3,4], x =\u003e x+2); // a is an iterator\nconst b = map(a, x =\u003e x*2); // b is also an iterator\nassertSequenceEquals(b, [6, 8, 10, 12]);\n```\n\nIn addition to supporting iterables \u0026 iterators, `Ferrum/Sequence` can take objects as input:\n\n```js\nconst {map,  iter, assertEquals, assertSequenceEquals} = require('ferrum');\n\nconst a = map({a: 42, b: 43}, ([k, v]) =\u003e v+2); // a is an iterator\nconst b = map(a, x =\u003e x*2); // b is also an iterator\nassertSequenceEquals(b, [88, 90]);\n\nconst obj = {foo: 23, bar: 24};\nconst log = [];\nfor (const [key, value] of iter(obj)) {\n  log.push(`${key} | ${value}`);\n}\n\nassertEquals(log, [\n  'foo | 23',\n  'bar | 24',\n]);\n```\n\n`Ferrum/Sequence` uses [lodash.isPlainObject](https://lodash.com/docs/4.17.11#isPlainObject)\nand always tries to use the iterator protocol to make sure object iteration is only used if\nit really should.\n\n```js\nconst {map, assertSequenceEquals} = require('ferrum');\n\nconst obj = {};\nobj[Symbol.iterator] = function*() {\n  yield 2;\n  yield 3;\n};\n\nassertSequenceEquals(map(obj, x =\u003e x*2), [4, 6]);\n```\n\n`Lodash` and `Underscore` only support arrays as input \u0026 output; `wu.js` supports iterators as input \u0026 output but has no\nsupport for plain objects.\n\n\u003ca name=\"reverse-currying\"\u003e\u003c/a\u003e\n#### Reverse Currying\n\n`Ferrum/Sequence` provides many higher order functions. These are functions that take other functions as parameters, like `map()` or `filter()`.\n\n```js\nconst {map, filter, assertSequenceEquals} = require('ferrum');\n\n// Map is used to change each value in a list/iterable\nassertSequenceEquals(map([1,2,3,4], x =\u003e x*2), [2,4,6,8]);\n\n// Filter removes elements in a list/iterable\nassertSequenceEquals(filter([1,2,3,4], x =\u003e x%2 === 0), [2, 4]);\n```\n\nSometimes it can be useful to create an intermediate function with just a\nfew arguments instead of calling the function right away:\n\n```js\nconst { map, plus, list, assertSequenceEquals } = require('ferrum');\n\nconst myList = [\n  [1,2,3],\n  [4,5,6],\n  [7,8,9]\n];\n\n// Add 2 to each number in a two dimensional list\n// This example uses currying twice: in the `plus(2)`\n// and in the `map()`\nconst a = map(myList, map(plus(2)));\nassertSequenceEquals(map(a, list), [\n  [3,4,5],\n  [6,7,8],\n  [9,10,11]\n]);\n\n// This is what the code would look like without currying:\n// A lot less convenient and harder to read\nconst b = map(myList, (sublist) =\u003e map(sublist, (b) =\u003e plus(b, 2)));\nassertSequenceEquals(map(b, list), [\n  [3,4,5],\n  [6,7,8],\n  [9,10,11]\n]);\n```\n\nYou may have noticed, that when currying is used, the arguments are given in reverse order; this is\nwhy we call it reverse currying. We have decided to use currying this way, because there should never\nbe extra arguments after the function (otherwise you end up with dangling arguments multiple lines below)\nwhile the function is usually also the first parameter you want to supply when currying:\n\n```js,notest\n// This is not very handy because you might need to scroll down to find the last\n// argument; you will also need to scroll down to determine whether the call to\n// each is using currying\neach(() =\u003e {\n  ...\n}, [1,2,3]);\n\n// This is much more handy\neach([1,2,3], () =\u003e {\n  ...\n});\n```\n\nUnderscore.js does not support currying at all; lodash provides curried variants of their functions in an extra\nmodule (not very handy either because it is often useful to mix curried and non currying invocations) while lodash\nhas opted to make the function the first parameter, delivering good support for currying and not so good support\nfor normal function invocation.\n\n\u003ca name=\"pipelining\"\u003e\u003c/a\u003e\n#### Pipelining\n\n`Ferrum` provides a function called `pipe()` which – together with currying – can be used to build complex data processing pipelines.\nPipelines are conceptually the same as the highly successful pipes in bash; the feature is currently being introduced into the JavaScript\nstandard library in the form of the [`|\u003e` operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Pipeline_operator).\n\n```js\nconst { sqrt, floor } = Math;\nconst {\n  pipe, filter, uniq, map, mul, mapSort, identity, take,\n  prepend, takeWhile, all, range, assertSequenceEquals,\n  extend, plus, any,\n} = require('ferrum');\n\nconst a = pipe(\n  [5,1,6,7,10,11,1,3,4],\n  filter(x =\u003e x%2 === 1), // Get rid of even number\n  uniq,                   // Get rid of duplicates\n  map(mul(3)),            // Multiply each element by three\n  mapSort(identity));     // Sort all numbers\nassertSequenceEquals(a, [3,9,15,21,33]);\n\n// Very simple primality test\nconst isPrime = (v) =\u003e v \u003e 1 \u0026\u0026 pipe(\n  range(2, floor(sqrt(v)) + 1),\n  map(x =\u003e v % x !== 0), // check that v is not divisible by x\n  all);\n\n// Sequence of all prime numbers (calculated slowly)\nconst primes = () =\u003e pipe(\n  range(0, Infinity),\n  filter(isPrime));\n\nassertSequenceEquals(take(primes(), 5), [2, 3, 5, 7, 11]);\n```\n\nLearning to write algorithms in this way is not always easy, but it can be very rewarding\nas the pipe form is often a lot more readable. To illustrate this, let's take a look how our\ncode of the prime sequence example changes as we take a way features from `Ferrum`;\nlet's first take away currying and the `pipe()` function itself:\n\n```js\nconst { sqrt, floor } = Math;\nconst { all, map, takeWhile, filter, range, assertSequenceEquals, take, extend, plus } = require('ferrum');\n\nconst isPrime = (v) =\u003e v \u003e 1 \u0026\u0026 all(map(range(2, floor(sqrt(v))+1), x =\u003e v % x !== 0));\nconst primes = () =\u003e filter(range(0, Infinity), isPrime);\n\nassertSequenceEquals(take(primes(), 5), [2, 3, 5, 7, 11]);\n```\n\nOne way to work around the lack of currying and `pipe()` is to just put all our\nfilter stages into one expression. Due to this, our code has become much shorter and much harder to read.\nLook at how the dataflow jumps around, see how distant the map function and it's argument are from each other\nand it does not help that subexpression cannot be properly documented any more.\nLet's try another way to write down these functions:\n\n```js\nconst { sqrt, floor } = Math;\nconst { assertSequenceEquals, all, map, takeWhile, filter, range, take } = require('ferrum');\n\nconst integers = () =\u003e range(1, Infinity);\nconst isPrime = (v) =\u003e {\n  const candidates = range(2, floor(sqrt(v)) + 1);\n  const tests = map(candidates, x =\u003e v % x !== 0)\n  return v \u003e 1 \u0026\u0026 all(tests);\n}\nconst primes = () =\u003e filter(integers(), isPrime);\n\nassertSequenceEquals(take(primes(), 5), [2, 3, 5, 7, 11]);\n```\n\nThis is much better! The data flow is more clear and substeps can be documented again.\nIn this version we used temporary variables to get around not having `pipe()` and currying;\nthis is much better than just putting everything into one line.\n\nNote how `positiveIntegers` became its own function while `fromTwo` and `candidates`\nbecame just local variables. Also note how `all` and `map` are still in the same expression.\nSometimes this is the more readable variant. We have to decide each time.\n\nThis variant still has disadvantages though; first of all the code still looks more\ncluttered and the dataflow still jumps around more than the pipe variant.\nYou also have to come up with a lot of names for temporary variables and take care\nnot to reuse them (since they are lazily evaluated they must only be used once).\nThis is one of the things you communicate by using `pipe()` over local variables: \"This variable\nwill never be used again\" – knowing this \u0026 limiting the number of variables in a scope can be\nvery useful, especially in large functions.\n\nFinally, let's implement this in classic imperative style:\n\n```js\nconst { sqrt } = Math;\n\nconst isPrime = (v) =\u003e {\n  if (v \u003c 2) {\n    return false;\n  }\n\n  for (let i=0; i \u003c= sqrt(v); i++) {\n    if (v % i !== 0) {\n      return false;\n    }\n  }\n\n  return true;\n}\n\nconst primes = function *primes() {\n  for (let i=0; true; i++) {\n    if (isPrime(i)) {\n      yield i;\n    }\n  }\n}\n```\n\nThe first thing that becomes noticeable in this version is that it is more\nthan twice as long as our variant using `pipe()` (not counting comment lines);\nthis version also uses two levels of nesting, while our pipelined version uses\njust one level of nesting. The imperative version also contains two for loops\nand three if statements; for loops are notoriously hard to read as well.\nFinally, the imperative version forces us to think in imperative terms – to consider\nwhat happens in each step of the for loop one by one and then come to the conclusion:\n*Ah, this for loop just gets rid of all those integers that are not prime*. In the imperative\nversion this intention must be deduced, in the pipelined version it is plain to see.\n\nTo sum it up, using `pipe()` and currying the functions from `Ferrum` has a number\nof advantages; you end up with fewer levels of nesting, can avoid a lot of branching (if statements)\nand hard to write for loops; pipelining let's you break apart your problem into multiple\nclearly defined transformation steps with obvious data flow and obvious intention.\n\nUnderscore, lodash and wu.js all allow you to do something similar with chaining which does work quite well.\nThey do require a bit more boilerplate since values need to be wrapped before chaining and unwrapped\nafter chaining has finished. Pipelining will have even less boilerplate when the `|\u003e` becomes\navailable and pipelining can be used with arbitrary transformation functions, while\nchaining can only be used with functions supported by the library, thus pipelining is much\nmore generic \u0026 extensible.\n\n\u003ca name=\"lazy-evaluation\"\u003e\u003c/a\u003e\n#### Lazy Evaluation\n\nLike Python iterators, sequences support lazy evaluation. They support it, because lazy evaluation\nis a core feature of JavaScript ES6 iterators.\n\nThis means that the values in iterators/sequences are only evaluated once they\nare needed:\n\n```js\nconst { map, plus, list, assertSequenceEquals } = require('ferrum');\nconst a = map([1,2,3], plus(2)); // At this point, no calculations have been performed\nconst b = list(a); // This will actually cause the values of the `a` iterator to be calculatedA\nassertSequenceEquals(a, []); // `a` is now exhausted and can no longer be used\nassertSequenceEquals(b, [3,4,5]); // `b` can be used as often as we want\nassertSequenceEquals(b, [3,4,5]);\n```\n\nTry the above example with a couple of `console.log` statements and see what happens.\n\nThe practical upshot of this property is that it becomes possible to work with infinite\nsequences, like the `primes()` sequence above. It can be more efficient as well, since\nvalues that are not needed are not computed.\n\n```js\nconst {take, list, assertSequenceEquals} = require('ferrum');\n\nfunction* fibonacci() {\n  let a=0, b=1;\n  while (true) {\n    yield a;\n    yield b;\n    a += b;\n    b += a;\n  }\n}\n\n// Even though fibonacci() is infinite, this just works because only the\n// first five fibonacci numbers are actually generated\n// Note that just list(fibonacci()) would crash the program since that would\n// require infinite memory and infinite time\nassertSequenceEquals(take(fibonacci(), 5), [0, 1, 1, 2, 3]);\n```\n\nUnderscore and lodash use arrays instead of iterators, so they have no lazy evaluation support.\nwu.js uses iterators and thus has full lazy evaluation support.\n\n\u003ca name=\"traits\"\u003e\u003c/a\u003e\n### Traits\n\n`Sequence/Traits` is the second big feature this library provides; it is a concept borrowed from\nthe Rust language. They let you declare \u0026 document a generic interface; like the `sequence` concept\nabove they are not an entirely new concept; while sequence is a library designed to make working\nwith the [JavaScript iteration protocols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols)\neasier, traits standardize the creation of JavaScript protocols itself, thereby reducing boilerplate.\nIndeed the `Sequence` Trait is just a wrapper over the `Iterable` protocol of JavaScript.\n\n```js\nconst {Trait} = require('ferrum');\n\n// Declaring a trait\n/**\n * The Size trait is used for any containers that have a known size, like\n * arrays, strings, Maps…\n * Size should be used only for sizes that are relatively quick to compute, O(1) optimally…\n * @interface\n */\nconst Size = new Trait('Size');\n\n// Using it\nconst size = (what) =\u003e Size.invoke(what);\nconst empty = (what) =\u003e size(what) === 0;\n\n// Providing implementations for own types; this implementation will be\n// inherited by subclasses\nclass MyType {\n  [Size.sym]() {\n    return 42;\n  }\n}\n\n// Providing implementations for third party types. These won't be inherited\n// by subclasses\nSize.impl(Array, (x) =\u003e x.length); // Method of type Array\nSize.impl(String, (x) =\u003e x.length);\nSize.impl(Map, (x) =\u003e x.size);\nSize.impl(Set, (x) =\u003e x.size);\n\n// This implementation just applies to plain objects.\nSize.impl(Object, (x) =\u003e {\n  let cnt = 0;\n  for (const _ in x) cnt++;\n  return cnt;\n});\n\n// Note: The two following examples would be a bad idea in reality,\n// they are just here toshow the mechanism\nSize.implStatic(null, (_) =\u003e 0); // Static implementation (for a value and not a type)\n```\n\nSome of the advantages of using Traits are illustrated for the code above:\nFirst of all, using traits saves us a bit of boilerplate code; by having an actual\nvariable representing the trait, we have a good place to document the trait; the `@interface`\njsdoc feature can be used for this. We can also use this documentation to specify laws that\nimplementations of the trait should abide by, like the (soft) law that `Size` implementations\nshould be quick to compute.\n\nTrait also features machinery to implement traits for third party types and even built in types\nlike `Array`, `Object`, `null` or `undefined`. The classic way to implement protocols does not work in these\ncases:\n\n```js\nconst Size = Symbol('Size');\n\n// Using just Symbols works perfectly for your own types\nclass MyType {\n  [Size]() {\n    return 42;\n  }\n}\n\n// Using symbols for third party types is suboptimal,\n// since we have to modify the type's prototype which\n// could lead to weirdness\nArray.prototype[Size] = () =\u003e this.length;\n\n// Using symbols for Object, is a very bad idea as the implementation\n// will be inherited by all other classes…this implementation obviously\n// is the wrong one for Set for instance.\n// This also illustrates why it is generally a bad idea to enable inheritance\n// for third party types\nObject.prototype[Size] = () =\u003e {\n  let cnt = 0;\n  for (const _ in this) cnt++;\n  return cnt;\n}\n\n// Using symbols on values like null or undefined will just lead to a TypeError\n// being thrown\n\n//null[Size] = () =\u003e 0; // throws TypeError\n```\n\nThe oldest pre-ES6 implementation just used method names; this strategy is very\nproblematic, since two different interfaces may use the same method name:\n\n```js\nclass MyDbTable {\n  size() {\n    return request(`https://mydb.com/${this._tableName}/size`);\n  }\n};\n\nclass MyLocalTable {\n  size() {\n    return this._payload.length;\n  }\n}\n```\n\nIn the hypothetical example above, one size() method returns an integer, while\nthe other returns a promise resolving an integer (which makes total sense since\nit's the size of some database table). Even though each method makes sense for itself,\nthere is no way to distinguish between them; the developer may write a function, expecting an integer…\n\nSince the method name `size()` is already taken, we cannot even implement the async\nsize interface for `MyLocalTable`.\n\nUsing traits we can actually encapsulate this relationship well:\n\n```js\n// dbtable.js\nconst { Trait, Size: SyncSize } = require('ferrum');\nconst Size = new Trait('Size');\n\nclass DbTable {\n  [Size.sym]() {\n    return request(`https://mydb.com/${this._tableName}/size`);\n  }\n};\n\nclass MyLocalTable {\n  [Size.sym]() {\n    return this._payload.length;\n  }\n}\n\nSize.implDerived([SyncSize], ([size], v) =\u003e Promise.resolve(size(v)));\n```\n\nThis example above illustrates how – using traits – we can not only deal with\nname collisions by just renaming traits on the fly, we were also able to write\na generic adapter that automatically implements `AsyncSize` for all types supporting `Size`.\n\nTo sum up, using Traits provides a number of advantages: Traits let you avoid\nsome boilerplate code, they allow you to specify and implement generic interfaces\nwithout the danger of name collisions; they let you provide implementations for third\nparty types, built-in types and even `null`, `undefined` and plain `Object` without\nmodifying these types.\nThey even let you write generic adapters, implementing traits for entire groups\nof traits at once.\n\n\u003ca name=\"operators-as-functions\"\u003e\u003c/a\u003e\n### Operators as functions\n\n`Ferrum/Ops` provides all of the JS operators and some extra boolean operators as curryable functions.\n\n```js\nconst { strictEqual: assertIs } = require('assert');\nconst { plus, and, not, is, xor, map, list, assertSequenceEquals } = require('ferrum');\n\nassertSequenceEquals(\n  map([1,2,3], plus(2)),   /* =\u003e */ [3,4,5]);\nassertIs(and(true, false), /* =\u003e */ false);\nassertIs(not(1),           /* =\u003e */ false);\nassertIs(is(2, 2),         /* =\u003e */ true);\nassertIs(xor(true, false), /* =\u003e */ true);\n```\n\n\u003ca name=\"typing-utilities\"\u003e\u003c/a\u003e\n### Typing utilities\n\nFerrum provides utilities for working with types that can be safely\nused with null and undefined.\n\n```js\nconst { strictEqual: assertIs } = require('assert');\nconst {isdef, type, typename} = require('ferrum');\n\nassertIs(isdef(0),         /* =\u003e */ true);\nassertIs(isdef(null),      /* =\u003e */ false);\nassertIs(isdef(undefined), /* =\u003e */ false);\n\nassertIs(type(22),        /* =\u003e */ Number);\nassertIs(type(null),      /* =\u003e */ null);\nassertIs(type(undefined), /* =\u003e */ undefined);\n\nassertIs(typename(type(22)),        /* =\u003e */ \"Number\");\nassertIs(typename(type(null)),      /* =\u003e */ \"null\");\nassertIs(typename(type(undefined)), /* =\u003e */ \"undefined\");\n```\n\nThe usual strategy of using `value.constructor` and `value.constructor.name`\nyields errors for `null` \u0026 `undefined`.\n\n\u003ca name=\"functional-utilities\"\u003e\u003c/a\u003e\n### Functional Utilities\n\n```js\nconst {\n  curry, pipe, filter, isdef, uniq, map, plus,\n  assertSequenceEquals, assertEquals,\n} = require('ferrum');\n\n// Using pipe() + auto currying instead of chaining\nassertSequenceEquals(\n  pipe(\n    [0, 1, 2, null, 3, 4, null, 5, 1, 3, 2, null, 1, 4],\n    filter(isdef), // Filter out every null \u0026 undefined\n    uniq,          // Remove duplicates\n    map(plus(2))), // Add two to each element\n  /* =\u003e */ [2, 3, 4, 5, 6, 7]);\n\n// Auto currying\nconst pair = curry('pair', (a, b) =\u003e [a, b]);\nassertEquals(pair(1,2),  /* =\u003e */ [1,2]);\nassertEquals(pair(2)(1), /* =\u003e */ [1,2]);\n```\n\n\u003ca name=\"changelog\"\u003e\u003c/a\u003e\n## Change Log\n\n### Features\n\n### 1.9.1\n\n* #193 fix: take(), tryTake(), takeWithFallback() no longer broken on plain\n  arrays. Congrats to @tobia for finding the first bug affecting the actual\n  code (not just documentation or CI).\n\n### 1.9.0\n\n* Hashable Trait \u0026 Hash tables ([3a86070](https://github.com/adobe/ferrum/commit/3a86070336d9a7f165e1d1d15b7858a0c1391c89))\n* apply1(), let_in(), call() ([3a86070](https://github.com/adobe/ferrum/commit/3a86070336d9a7f165e1d1d15b7858a0c1391c89))\n* create(), createFrom(), builder() ([3a86070](https://github.com/adobe/ferrum/commit/3a86070336d9a7f165e1d1d15b7858a0c1391c89))\n\n\n### 1.8.0\n\n* Move many tests into the documentation examples ([c033897](https://github.com/adobe/ferrum/commit/c033897fc9bc224))\n\n### 1.7.0\n\n* Use ferrum.doctest to make sure examples are valid js code ([b0f9d45](https://github.com/adobe/ferrum/commit/b0f9d45))\n\n\n### 1.6.0\n\n*  Add mutate(), apply() ([8d28f73](https://github.com/adobe/ferrum/commit/d4e3a7a750afe58696097b5f75117c555291d01b))\n\n### 1.5.0\n\n* Alias flatten() -\u003e flat() ([2abad3f](https://github.com/adobe/ferrum/commit/2abad3f4cc72bbad7ee19da8f59d4917))\n* group(), multiline() and takeUntil() ([0bc0ca0](https://github.com/adobe/ferrum/commit/0bc0ca0059b6a7f8f61cf))\n\n\n### 1.4.0\n\n*  Add intersperse() ([8d28f73](https://github.com/adobe/ferrum/commit/8d28f73))\n\n### 1.3.0\n\n* Add function repeatFn() ([81de232](https://github.com/adobe/ferrum/commit/81de232))\n* Provide chunkify functions ([9ff9603](https://github.com/adobe/ferrum/commit/9ff9603))\n* Provide takeShort() \u0026 takeWithFallback() ([bafa834](https://github.com/adobe/ferrum/commit/bafa834))\n* slidingWindow now returns an empty sequence if no=0 ([533cff4](https://github.com/adobe/ferrum/commit/533cff4))\n\n### 1.2.0\n\n* **Bugfix: Support for objects with Symbol keys** – Before this change\n  most functions would disregard Symbol keys in objects. E.g. `size({[Symbol()]: 42})`\n  would return zero. Now the functions\n  `pairs(), keys(), values(), size(), empty(), shallowclone(), deepclone(), iter(),\n   each(), replace(), setdefault(), del(), assign(), has(), get(), eq, uneq,\n   assertEquals, assertUneq` are explicitly tested and with objects containing symbol keys.\n\n\u003ca name=\"development\"\u003e\u003c/a\u003e\n## Development\n\n\u003ca name=\"build\"\u003e\u003c/a\u003e\n### Build\n\n```bash,notest\n$ npm install\n```\n\n\u003ca name=\"test\"\u003e\u003c/a\u003e\n### Test\n\n```bash,notest\n$ npm test\n```\n\n\u003ca name=\"lint\"\u003e\u003c/a\u003e\n### Lint\n\n```bash,notest\n$ npm run lint\n```\n","funding_links":[],"categories":["JavaScript","javascript","Libraries"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadobe%2Fferrum","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadobe%2Fferrum","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadobe%2Fferrum/lists"}