True Myth 1.3.0 and 2.0.0
A couple nice ergonomic updates and some breaking changes for consuming the library.
Today I released two versions of True Myth: 1.3.0 and 2.0.0. You can read the 1.0 announcement from last November for an overview of the library and a discussion of why you might want to use the library in the first place!
Since its initial release last November, True Myth has gone through a number of small feature and bug fix releases, each of which is more interesting in its own right than 2.0 is—because there are almost no new “features” here, and the changes to the functionality which are in 2.0 are purely additive and could readily have gone in 1.3 instead.
In fact, the act of writing that sentence made me realize that there really should be a 1.3 which people can trivially upgrade to and then take on the changes in 2.0 later.
– 1.3.0 –
There are a few very small changes in 1.3 that are just nice ergonomic wins. (You may also be interested in looking back at the list of other releases to see what else has landed since 1.0.)
Expose value
and error
The value
property in Maybe.Just
and Result.Ok
instances, and the error
property in Result.Err
instances, are now public, readonly properties instead of private properties. I made those private in the initial implementation because I thought it made more sense to expose them via methods, but experience showed that this is a relatively common pattern in practice:
function dealsWithAMaybe(couldBeAString: Maybe<string>) {
if (couldBeAString.isJust()) {
console.log(`It was! ${couldBeAString.unsafelyUnwrap()}`);
}
}
This is a contrived example of course, but I and my colleagues found in practice that this is a scenario that comes up relatively often, especially when integrating with existing code rather than writing new code – control flow patterns there tend to assume early-return-on-null
or similar instead.
So I made a change (leaning on TypeScript’s notion of “type narrowing”) so that you don’t have to use unsafelyUnwrap
in this scenario anymore! You can use the method types, the standalone functions, or direct matching against the variants on the property
import Maybe from 'true-myth/maybe';
function dealsWithAMaybe(maybe: Maybe<string>) {
if (maybe.isJust()) {
console.log(`It was! ${maybe.value}`);
}
}
In the Result
case this is even nicer (notice that I’m using the variant, rather than a function, to discriminate between the two and narrow the types here):
import Result, { Variant } from 'true-myth/result';
function dealsWithAResult(result: Result<string, Error>) {
if (result.variant === Variant.Ok) {
console.log(`Huzzah: ${result.value}`);
} else {
console.log(`Alas: ${result.error.message}`);
}
}
Basically: you now have more options for handling these scenarios, a nicer API, and—not that it should usually matter that much, but for whatever it’s worth—better performance by way of doing things with property lookups instead of function invocations in quite a few places.1
Static helper methods
At my friend and collaborator Ben Makuh’s suggestion, I built a couple static helper methods to go with those. These helpers just give you nice abstractions to drop into functional pipelines. For example, you can lean on the type-narrowing capabilities described above while working through a list of Maybe
s to know that an item is a Just
and use the new Just.unwrap
static method in the pipeline:
import Maybe, { Just } from 'true-myth/maybe';
function justLengths(maybeStrings: Array<Maybe<string>>) {
return maybeStrings
.filter(Maybe.isJust)
.map(Just.unwrap)
.map(s => s.length);
}
Analogous helpers exist for Result
in the form of the Ok.unwrap
and Err.unwrapErr
methods. (Nothing
has no analog for what I hope are obvious reasons!)
Tweaks to the variant
properties
The variant
property on both Maybe
and Result
has changed in two ways:
It is now
readonly
. This was an implicit invariant previously—you would break everything in the library if you changed thevariant
value—and I’ve just made it explicit in the type system.It is now properly constrained with a literal type on the concrete instances. That is, the type of
Just.variant
is no longerVariant
but specificallyVariant.Just
. (This is what enables you to use the variant for narrowing as demonstrated above. I should have done this in 1.0, and just forgot to!)
And that’s it for 1.3.0!
– 2.0.0 –
The 2.0 release is identical in features with the 1.3 release. However, it makes a breaking change to how consumers interact with the application, requiring updates to your tsconfig.json
file and your bundler configuration, and removing support for Flow types.
Configuration file updates
Getting True Myth working nicely with consuming TypeScript packages has been a source of frustration for me and others. In short, requiring you to use the "paths"
key in the "compilerOptions"
section of the tsconfig.json
made for an annoying amount of setup work, and it meant that using True Myth in a library required you to set it up in any consuming app. No good.
For type resolution to Just Work™, the types must be at the root of the distributed package.
As a result, I’ve stopped using libkit, which put the generated types in a reasonable-seeming but (in my experience) painful-to-use place, and have simplified the build layout substantially.
- The types themselves are generated only when publishing an update to npm. They go in the root at that point, and they get cleaned up after publishing. (This is pretty much identical to the solution we came up in ember-cli-typescript.)
- The other build files no longer get dropped in a nested
src
directory. - Since I was already at it, I renamed the two build directories from
commonjs
tocjs
and frommodules
toes
So the distributed build now looks something like this:
/
index.d.ts
maybe.d.ts
result.d.ts
unit.d.ts
utils.d.ts
dist/
cjs/
index.js
maybe.js
result.js
unit.js
utils.js
es/
index.js
maybe.js
result.js
unit.js
utils.js
You’ll just need to completely remove the "paths"
mapping for True Myth from your tsconfig.json
and, if you’ve done anything unusual with it, update your bundler configuration to point to the new build location, i.e. dist/commonjs/src
should now just be dist/cjs
. Bundlers which respect the modules
key in package.json
will pick it up automatically, as will Ember CLI.
Removing Flow types
To my knowledge, no one is actually using the Flow types for the library. When I first started on it, my collaborator Ben Makuh was using Flow, but he ended up migrating to TypeScript in the intervening time, and there are no consumers I know of. I was always relatively unsure of their correctness, and I don’t have a good way to validate their correctness, and maintaining them involved doing manual work on every release to update the types by hand.
If you do use True Myth with Flow, and you’re missing the types, please let me know. I just can’t maintain them myself at this point!
And that’s it! We’ve been using True Myth in production at Olo for quite some time, and it’s proved to be a really valuable tool. Give it a spin and let me know how these latest versions work for you!
I’ve made some changes under the hood to take advantage of this as well, so the library should be faster. Probably trivially faster, but my philosophy around library code is very much be as fast as you can; it’s a way of considering the people using your code—not just the developers, but the end users.↩