Documentation Testing & Hashing with Javascript (Ferrum.js 1.9)

7 min read Original article ↗

This post discusses the most recent additions to the ferrum javascript library: Test driven documentation, a reliable infrastructure hashing infrastructure including Hash Tables.

Karolin Varner

Press enter or click to view image in full size

It’s been a while since the initial release of ferrum. Over 400 stars on Github, one hundred dependants and just eight bug reports sure have proven that it’s features, and it’s sound API design provide a welcome addition to the library landscape of the javascript ecosystem.

My own projects have benefited most from ease with which data can be processed using pipe() and sequence transformers. The isdef() function is another hidden gem; while it is the tiniest part of the library, its approach to soundness in relation to null and undefined values has informed the API design of ferrum and elevated code I wrote outside the library Perhaps it is only appropriate, that one of the best things about a library designed for functional programming and modularity would be one of it’s smallest parts.

Still, a great number of useful features could be added; a way of defining classes that is more useful for functional programming or generalizing the sequence API to asynchronous sequences are just some of the more interesting ideas that might be added in the future. Feel free to jump in and share your view on these concepts; designing sound APIs for these use cases is proving to be really tough, so some input would be appreciated.

Other issues are low hangingfruits; waiting for pull requests by anyone trying to hone their functional programming skills!

So while new features are probably coming, for now Traits will probably remain the single most complex and innovative feature in ferrum. Traits are really useful as a basic building block and took a lot of careful design, the actual implementation though was not very hard to write: just a few lines of code…and then came edge cases Like a lot of functional programming concepts, traits really starts becoming useful once you understand how many features can be implemented using a common concept.

Achieving a lot with very little code; which is much harder than it sounds, after all writing a lot of code is much easier than writing very little. This is the reason, Ferrum was always supposed to be an educational tool too; providing an explanation so users of th library have an easier time understanding why a particular component is useful and in what situations.

That requires a lot of documentation and — because too much text is simply hard to read — that means examples!

Tests for the Examples

Sadly, the examples are one area where the soundness promise of ferrum really fell short; some examples provided in ferrum contained bugs! You would copy them, try to get run the example and be greeted by a syntax error first thing. Not very welcoming, not very helpful and — surprisingly — an issue that isn’t as easy to fix as it may seem! Since examples are part of the documentation checking for issues involves copying to a file, fixing the problem and copying the example back into the documentation. A process that is tedious and prone to errors. I am a very lazy person, so I didn’t do that. Instead, I did what rust does and rust executes examples as part of the tests and so does ferrum — now!

And if you find yourself in the same pinch, so can you!

I couldn’t find a tool that tests examples for Javascript, so I just built one: ferrum.doctest is a companion tool that can be used independently of ferrum; it runs tests in your readme and in your api documentation (supporting both markdown code blocks as well as jsdoc’s@example tags) and generates a file that can be fed into the testing framework of your choice. By default, files compatible with mocha are produced, but you can provide a custom tempate to make this work with any other test framework. Source maps make sure you can easily pinpoint which line in your examples produce errors. (Support for source maps in development tools is surprisingly spotty though which is why I am still trying to find a development toolset that works well in this setting.)

Since ferrum version 1.7 all examples are being tested using ferrum.doctest so I am happy to announce that buggy examples should be a thing of the past!

Hashing & Hash Tables

While documentation tests are extremely useful, I found them quite tedious to implement. So much practical development… To add a little excitement (or whatever weird things I consider to be exciting) I decided to implement a new Hashing infrastruct… — oh come on, don’t judge we all like to play with something fun once in a while and for some of us that means hash functions!

Hash functions are useful in a lot of contexts; not having a hashing infrastructure is something you can usually work around, but once you realize how useful this is you never want to go back. I myself have python to thank for introducing me to the concept when I started programming.

The most common use case for hash functions is their use in hash tables. And now ferrum provides one ( ferrum.HashMap — check out the examples, now properly tested). In a nutshell, hashmaps are similar to ES6 Maps or Objects; they are key/value stores; what differentiates the three is what types of key they allow.

Objects allow only Strings, Numbers and Symbols as keys.
ES6 Map allows arbitrary Objects, but the keys need to be identical. (E.g. two empty objects would map to different keys).
HashMap allows arbitrary Objects, keys need tobe equal but cannot store symbols. Two empty objects would always map to the same value!

HashMap is more powerful and matches the intention of the developer more closely in most cases. Most problems could be solved with a normal Map too by serializing your keys into a string but that is tedious; basically you are implementing your own hash function on the fly using an API that is not up to the tasks! Since HashMap implements the same interface as Map it should be safe to just substitute HashMap in many cases, as long as the keys being stored implement Hashable and even that requirement can be gotten around by integrating the object-hash library.

Why not just use object-hash?

You might also wonder why object-hash is not being used by default, given that it has been the de-facto standard of hashing in javascript for the last years. The reasoning is the same as the reasoning why ferrum provides its own equals function: In a nutshell, standard javascript lacks traits and while interfaces like the iterator protocol provide a similar functionality, interfaces on their own have drawbacks like being unusable for plain objects. This is a big problem and led to a situation where libraries like object-hash had to provide a heuristic for working with third party types; these heuristics usually work, but when they don’t you are out of luck without a good, clean way to solve that situation.

Traits in ferrum solve all these problems; using traits a lot of functions like eq(), shallowclone() or now hash() can be implemented as cleanly, recursive polymorphic functions. Big words, they really just mean that you can supply your own implementation for your own types and that classes can be hashed or compared by comparing all their fields.

So functions in ferrum are very reliable, forcing you to explicitly specify how they should work for third party types. And since this is tedious ferrum is designed to be flexible enough to allow you to also integrate libraries using heuristics — like object hash. Which means you get the best of both worlds!

I am considering creating yet another companion library — ferrum.heuristic — which implements heuristic extensions for all the traits in ferrum. Give your thumbs up on this issue if you would like to accelerate development!

Next Steps

Ferrum has done a lot to improve the stability of projects I worked on in the last year; with documentation testing and a hashing infrstructure two big gaps in the javascript library ecosystem have been filled. Two features that should have been part of the js standard library all along, making your life as a developer considerably easier.

More are already being planned like extending the sequence functions to asynchronous sequences. Join the discussion and tell us which of these features would you find most useful?