<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Perfective]]></title><description><![CDATA[Perfective]]></description><link>https://blog.perfective.dev</link><image><url>https://substackcdn.com/image/fetch/$s_!QphL!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50a9c214-0bb1-4d5f-a61c-00b3612281cd_1000x1000.png</url><title>Perfective</title><link>https://blog.perfective.dev</link></image><generator>Substack</generator><lastBuildDate>Mon, 20 Apr 2026 04:48:12 GMT</lastBuildDate><atom:link href="https://blog.perfective.dev/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Andrey Mikheychik]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[perfective@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[perfective@substack.com]]></itunes:email><itunes:name><![CDATA[Andrey]]></itunes:name></itunes:owner><itunes:author><![CDATA[Andrey]]></itunes:author><googleplay:owner><![CDATA[perfective@substack.com]]></googleplay:owner><googleplay:email><![CDATA[perfective@substack.com]]></googleplay:email><googleplay:author><![CDATA[Andrey]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Announcing @perfective/common v0.10.0]]></title><description><![CDATA[Towards the best `Result`]]></description><link>https://blog.perfective.dev/p/perfective-common-v0-10-0-announcement</link><guid isPermaLink="false">https://blog.perfective.dev/p/perfective-common-v0-10-0-announcement</guid><dc:creator><![CDATA[Andrey]]></dc:creator><pubDate>Fri, 01 Dec 2023 11:53:45 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/193fe1ec-12f5-4be5-87db-3b6857d27ea7_560x400.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>After eight months of development, the new <a href="https://github.com/perfective/ts.common/releases/tag/v0.10.0">0.10.0</a> version of the <a href="https://github.com/perfective/ts.common">@perfective/common</a> package is available.</p><p>This release has focused on three major things:</p><ul><li><p>Getting the <code>Result</code> monad on par with the <code>Maybe</code> monad.</p></li><li><p>Introducing the new <code>@perfective/common/date</code> package.</p></li><li><p>Organizing the planning process to move towards the v1.0 release.</p></li></ul><p>Minor changes and improvements were the result of the major changes.</p><h1>Improved Result monad</h1><p>The @perfective/common/result monad with the Result monad was introduced in the <a href="https://github.com/perfective/ts.common/releases/tag/v0.9.0">v0.9.0</a> release. The <code>Result</code> is a concrete case of the <code>Either</code> monad holding a <code>Success</code> value or a <code>Failure</code> error.</p><p>In v0.9.0, the <code>Result</code> class had only four methods:</p><ul><li><p><code>Result.onto()</code> as the <em>bind</em> operator (&gt;&gt;=)</p></li><li><p><code>Result.to()</code> as the <em>fmap</em> and <em>bimap</em> functions.</p></li><li><p><code>Result.into()</code> as the <em>fold</em> method.</p></li><li><p><code>Result.through()</code> to apply the value to a given procedure.</p></li></ul><p>In v0.10.0, the <code>Result</code> class was extended with the filtering methods:</p><ul><li><p><code>Result.that()</code> checks the <code>Success</code> value with a given <code>Predicate</code> and either returns the same <code>Success</code> or a <code>Failure</code> with a given <code>Error</code>.</p></li><li><p><code>Result.which()</code> works the same way as Result.that(), but allows passing a type guard and narrows the value type.</p></li><li><p><code>Result.when()</code> keeps the Success or switches to a Failure based on the external condition.</p></li></ul><p>And with the recovery methods:</p><ul><li><p><code>Result.or()</code> &#8211; allows terminating the <code>Result</code> chain and fallback to a recovery value.</p></li><li><p><code>Result.otherwise()</code> &#8212; allows to fallback to a recovery value and continue the <code>Result</code> chain.</p></li></ul><p>In this form, the <code>Result</code> monad achieves nearly full parity with the <code>Maybe</code> monad (except for the <code>Maybe.pick()</code> method).</p><p>Consider an HTTP endpoint implementation to load a user from a database by an unsafe user id input.</p><p>Assume you have the following functions:</p><pre><code>export interface User {
    // User data
}

/** Returns `true` if the active user has admin access. */
declare function hasAdminAccess(): boolean;

/** Builds an SQL query to load a user with a given `id`. */
declare function userByIdQuery(id: number): string;

/** Sends a given `sql` to the database and returns a User. */
declare function userQueryResult(sql: string): Promise&lt;User&gt;;

/** Logs a given error */
declare function logError(error: Error): void;</code></pre><p>With the new Result functionality, it is now possible to express validation and transformation of a value in one chain.</p><pre><code>import { isNotNull } from '@perfective/common';
import { typeError } from &#8216;@perfective/common/error&#8217;;
import { naught } from '@perfective/common/function';
import { decimal, isNonNegativeInteger } from '@perfective/common/number';
import { rejected } from '@perfective/common/promise';
import { Result, success } from '@perfective/common/result';
import { isString } from '@perfective/common/string';

function validUserId(id: unknown): Result&lt;number&gt; {
    return success(id)
        .which(isString, typeError('Input must be a string'))
        .to(decimal)
        .which(isNotNull, 'Failed to parse user ID')
        .that(isNonNegativeInteger, 'Invalid user ID');
}

async function userResponseById(id: unknown): Promise&lt;User&gt; {
    return success(id)
        .when(hasAdminAccess, 'Access Denied')
        .onto(validUserId)
        .to(userByIdQuery)
        .through(naught, logError)
        .into(userForQuery, rejected);
}</code></pre><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://blog.perfective.dev/p/perfective-common-v0-10-0-announcement?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Share your excitement about the Result monad with friends and colleagues!</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.perfective.dev/p/perfective-common-v0-10-0-announcement?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.perfective.dev/p/perfective-common-v0-10-0-announcement?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p></div><h2>Breaking changes</h2><p>After introducing the <code>Result.otherwise()</code> method, the <code>recovery()</code> function was deprecated.</p><p>The <code>resultFrom()</code> function has been changed to wrap functions that may throw an error into a <code>try-catch</code> block and return a <code>Success</code> or a <code>Failure</code>. It allows the creation of the <code>Result.onto()</code> method callbacks.</p><h2>Future considerations</h2><p>The major question for future improvement is whether some <code>Result</code> methods should use the <code>try-catch</code> blocks and guarantee a safe return to the computation chain. The concern is a possible performance hit.</p><p>Another area of improvement is interoperability with the <code>Promise</code> chains. A <code>Result</code> is just a synchronous version of a <code>Promise</code>, and the ability to switch a <code>Promise</code> to a <code>Result</code> and back would be useful.</p><h1>New Date package</h1><p>The new <a href="https://github.com/perfective/ts.common/blob/main/src/date/index.adoc">@perfective/common/date</a> package introduces several functions for the <code>Date</code> class and the <code>Timestamp</code> alias for a <code>number</code>.</p><p>A <code>Date</code> can now be instantiated using the <code>date()</code> constructor function. It supports <code>Date</code>, <code>string</code>, and <code>Timestamp</code> (<code>number</code>) inputs, but unlike the <code>new Date()</code> constructor, the <code>date()</code> function returns <code>null</code> instead of an &#8220;Invalid date&#8221; <code>Date</code>.</p><p>Two edge case constructors for a <code>Date</code> are also available: the <code>now()</code> and <code>epoch()</code> functions. The <code>now()</code> function uses the <code>Date.now()</code> static method, so the current date/time can be mocked.</p><p>A <code>Timestamp</code> can be extracted from a <code>Date</code> or parsed from a string using the timestamp() constructor function. Like the <code>date()</code> function, it returns <code>null</code> for invalid dates and unparseable inputs.</p><p>Both the <code>date()</code> and <code>timestamp()</code> functions are designed to return <code>null</code> in case of a failure, allowing convenient use in the <code>Maybe</code> chain.</p><h2>Future considerations</h2><p>While returning null for invalid values is acceptable, there may be better approaches. Throwing an error instead seems more reasonable, as it provides a clear cause of a failure.</p><p>But throwing an error would require extensive use of <code>try-catch</code> or using the <code>Result</code> monad (especially if it will handle the <code>try-catch</code> internally).</p><p>A riskier alternative is to rethink the <code>Maybe</code> and <code>Result</code> packages roles in the <code>@perfective/common</code>. Currently, both monads are considered high-level dependencies: they cannot be used in any other sub-packages (except for other high-level dependencies, like <code>Match</code>).</p><h1>Reorganized roadmap</h1><p>The <a href="https://github.com/perfective/ts.common/blob/main/ROADMAP.adoc">roadmap</a> was reorganized from scratch. It now contains all built-in JavaScript objects, their properties, methods, and a link to their earliest ECMAScript specification sections.</p><p>The reorganization goal was to document mapping between a property or a method of the built-in objects and a corresponding method(s) in the <code>@perfective/common</code> library.</p><p>While working on the roadmap, some general rules for the v1.0 release were described. As originally planned, the <code>@perfective/common</code> library targets ECMAScript 6 (aka ECMAScript 2015) with some support for the later ECMAScript versions.</p><p>For example, ECMAScript 2023 introduced the <code>Array.prototype.toReversed()</code> and <code>Array.prototype.toSorted()</code> methods. The <code>reversed()</code> and <code>sorted()</code> function already exist in the <a href="https://github.com/perfective/ts.common/blob/main/src/array/index.adoc">@perfective/common/array</a> package.</p><p>At the same time, it became clear that support of the binary-focused types and functions (like <code>TypedArray</code> won&#8217;t be included in the v1.0. The <code>@perfective/common</code> library is focused on making business application code readable. Binary-focused functions are used for lower-level code and are rarely used directly in the business-level code.</p><h2>Future considerations</h2><p>There are only a few major features required to tag v1.0:</p><ul><li><p>Internationalization/Localization support based on the built-in <code>Intl</code> objects.</p></li><li><p><code>Map</code> and <code>Set</code> support.<br>The challenge is to decide how to support both the <code>Maybe</code> and <code>Result</code> monads for <code>Map</code> and <code>Set</code>.</p></li><li><p><code>URI</code> and <code>URL</code> support.<br>Strictly speaking, the <code>URL</code> class is not a built-in object but is widely supported and can be a part of the library.</p></li><li><p>An <code>Input</code> (validation) monad to replace the existing input functions.</p></li></ul><p>Additionally, the problem of returning <code>null</code> vs. throwing an error must be resolved across the whole library.</p><h1>Improvements</h1><p>The <code>Panic</code> type and the&nbsp;<code>panic()</code> function from <a href="https://github.com/perfective/ts.common/blob/main/src/error/index.adoc">@perfective/common/error</a> now support cause: <code>Error</code>. This change allowed the deprecation of <code>Rethrow</code> and <code>rethrow()</code>.</p><p>The <a href="https://github.com/perfective/ts.common/blob/main/src/boolean/index.adoc">@perfective/common/boolean</a> package now provides the <code>isBoolean()</code>/<code>isNotBoolean()</code> type guards and the <code>isTruthy()</code>/<code>isFalsy()</code> functions.</p><h2>Documentation</h2><p>All exported types and functions now have a JSDoc comment, so the purpose and logic of the function can be checked in your IDE without reading the code.</p><h2>TypeScript 5.3 compatibility</h2><p>TypeScript v5.3 introduced a type inference change that broke <code>@perfective/common/maybe</code> <code>nothing()</code> and <code>nil()</code> functions. The code like:</p><pre><code>const maybe: Maybe&lt;number&gt; = nothing();</code></pre><p>started to fail, as the <code>nothing()</code> return type was inferred as <code>number | null | undefined</code> instead of just <code>number</code>.</p><p>To ensure backward compatibility, the <code>nothing()</code> and <code>nil()</code> functions now return the <code>Nothing&lt;Present&lt;T&gt;&gt;</code> type.</p><h1>Deprecations</h1><p>The <code>naught()</code> function from <a href="https://github.com/perfective/ts.common/blob/main/src/maybe/index.adoc">@perfective/common/maybe</a> is renamed into the <code>nil()</code> function (as the different <code>nil()</code> function was deprecated in v0.9.0 and is now removed).&nbsp;</p><p>The <code>empty()</code> function from <a href="https://github.com/perfective/ts.common/blob/main/src/function/index.adoc">@perfective/common/function</a> is renamed into the <code>naught()</code> function.</p><p>The <code>Rethrow</code> type and <code>rethrow()</code> function from <a href="https://github.com/perfective/ts.common/blob/main/src/error/index.adoc">@perfective/common/error</a> are deprecated in favor of the existing <code>Panic</code> and <code>panic()</code>.</p><p>The <code>unknownError()</code> function from <a href="https://github.com/perfective/ts.common/blob/main/src/error/index.adoc">@perfective/common/error</a> is renamed into the <code>caughtError()</code> function.</p><p>The <code>isTruthy()</code> and <code>isFalsy()</code> functions are moved from <a href="https://github.com/perfective/ts.common/blob/main/src/object/index.adoc">@perfective/common/object</a> into <a href="https://github.com/perfective/ts.common/blob/main/src/boolean/index.adoc">@perfective/common/boolean</a>.</p><h1>Supported Node.js versions</h1><p>Node.js 16 is no longer supported, as it is no longer an LTS version. Only Node v18 and v20 are used for the CI builds.</p><h1>Removed code</h1><p>Functions and types deprecated since v0.9.0 are now removed:</p><ul><li><p><code>arrayFromArrayLike()</code>, <code>arrayFromIterable()</code>, <code>flatten()</code> from <code>@perfective/common/array</code>.</p></li><li><p><code>Match.that()</code>, <code>Match.to()</code>, <code>Statement</code>, <code>StatementEntry</code>, <code>statements</code>, and <code>When.then()</code> from <code>@perfective/common/match</code>.</p></li><li><p><code>Maybe.lift()</code>/<code>lift()</code>, <code>Maybe.run()</code>/<code>run()</code>, <code>Nullable</code>, <code>Nil</code>, <code>Only</code>/<code>Solum</code>, <code>nullable()</code>, <code>nil()</code>, <code>only()</code>/<code>solum()</code>, <code>Optional</code>, <code>None</code>, <code>Some</code>, <code>optional()</code>, <code>none()</code>, <code>some()</code> from <code>@perfective/common/maybe</code>.</p></li><li><p><code>Run</code> and <code>result()</code> from <code>@perfective/common/promise</code>.</p></li><li><p><code>Output</code>, <code>isNotOutput()</code>, <code>isOutput()</code>, <code>output()</code> from <code>@perfective/common/string</code></p></li></ul><h1>What&#8217;s Next?</h1><p>The next version will be focused on:</p><ul><li><p>Finishing the Date class support (the <code>@perfective/common/date</code> package)</p></li><li><p>Introducing localization support (the <code>@perfective/common/i18n</code> package)</p></li><li><p>Improving consistency of the existing packages.</p></li></ul><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.perfective.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption"><strong>Subscribe now to receive the latest updates!</strong></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item></channel></rss>