Aller au contenu principal

Zygos

ζυγός - « balance »

Gestion fonctionnelle des erreurs. Alternatives légères à Neverthrow et fp-ts (Either, Task, TaskEither).

Zygos apporte les patterns de gestion fonctionnelle des erreurs à TypeScript sans le poids d'une bibliothèque FP complète. Au lieu de blocs try/catch qui perdent l'information de type, Zygos utilise les types Result, Option et Either pour rendre les erreurs explicites dans les signatures de vos fonctions. Chaque chemin d'échec est visible par le compilateur, vous gérez donc les erreurs avant qu'elles n'atteignent la production.


Quelques exemples

Le type Result représente une opération qui peut réussir (Ok) ou échouer (Err). Le pattern matching sur le résultat vous force à gérer les deux cas, éliminant les exceptions non gérées :

import { ok, err, Result } from "pithos/zygos/result/result";

function divide(a: number, b: number): Result<number, string> {
if (b === 0) return err("Division par zéro");
return ok(a / b);
}

const result = divide(10, 2);

if (result.isOk()) {
console.log(result.value); // 5
} else {
console.log(result.error); // Jamais atteint
}

Monades disponibles

MonadeDescriptionCas d'usage
ResultOk<T> ou Err<E>Opérations qui peuvent échouer
OptionSome<A> ou NoneValeurs optionnelles (pas de null)
EitherLeft<E> ou Right<A>Branchement générique à deux cas
TaskCalcul async paresseuxOpérations async différées
TaskEitherEither asyncOpérations async qui peuvent échouer

Result

Le Result de Zygos est 2.6x plus petit (~774 B vs ~1.96 kB) que Neverthrow, tout en maintenant une compatibilité API à 100%. Vous obtenez la même expérience développeur avec une fraction du coût en bundle.

Interchangeabilité avec Neverthrow

Pour migrer de Neverthrow vers Zygos, rien de plus simple : remplacez les imports et le tour est joué.

// Avant (Neverthrow)
import { ok, err, Result, ResultAsync } from "neverthrow";

// Après (Zygos) : changez uniquement l'import, le code reste IDENTIQUE
import { ok, err, Result } from "pithos/zygos/result/result";
import { ResultAsync } from "pithos/zygos/result/result-async";

// Votre code existant fonctionne sans changement
Migration

Rechercher & remplacer from "neverthrow" → séparer en les deux imports ci-dessus. Toutes les méthodes fonctionnent pareil.

Utilisation

Les Results supportent le chaînage avec map, mapErr et andThen. Chaque opération transforme la valeur de succès tout en propageant automatiquement les erreurs, similaire à la façon dont les Promises chaînent avec .then() :

import { ok, err, Result } from "pithos/zygos/result/result";
import { ResultAsync } from "pithos/zygos/result/result-async";

// Sync
const success = ok(42);
const failure = err("Quelque chose s'est mal passé");

// Le chaînage transforme la valeur Ok à travers chaque étape.
// Si une étape retourne un Err, la chaîne court-circuite.
ok(5)
.map(x => x * 2) // Ok(10)
.mapErr(e => `Erreur : ${e}`) // Toujours Ok(10)
.andThen(x => ok(x + 1)); // Ok(11)

// ResultAsync encapsule les Promises dans le même pattern Result,
// pour que les opérations async aient aussi une gestion d'erreurs typée.
const asyncResult = ResultAsync.fromPromise(
fetch("/api/data"),
() => "Erreur réseau"
);

Option

Gérez les valeurs optionnelles sans null/undefined. Le type Option rend l'absence de valeur explicite dans le système de types, remplaçant les types nullable par Some (valeur présente) ou None (valeur absente) :

import { some, none, fromNullable, Option, map, flatMap } from "pithos/zygos/option";
import { pipe } from "pithos/arkhe/function/pipe";

const value = some(42);
const empty = none;

// Depuis nullable
const maybeNull: string | null = "hello";
const opt = fromNullable(maybeNull); // Some("hello")

// Pattern matching
const result = isSome(opt)
? opt.value
: "default";

// Chaînage avec pipe
pipe(
some(5),
map(x => x * 2), // Some(10)
flatMap(x => some(x + 1)) // Some(11)
);

Either, Task, TaskEither

Implémentations légères basées sur fp-ts, 100% compatibles API. Ces monades couvrent des patterns de programmation fonctionnelle plus avancés : Either pour le branchement générique à deux cas, Task pour les calculs async paresseux, et TaskEither pour les opérations async qui peuvent échouer.

Interchangeabilité avec fp-ts

Migrer de fp-ts vers Zygos est tout aussi simple : remplacez les imports et c'est tout.

// Avant (fp-ts)
import * as E from "fp-ts/Either";
import * as T from "fp-ts/Task";
import * as TE from "fp-ts/TaskEither";

// Après (Zygos) : changez uniquement l'import, le code reste IDENTIQUE
import * as E from "pithos/zygos/either";
import * as T from "pithos/zygos/task";
import * as TE from "pithos/zygos/task-either";

// Votre code existant fonctionne sans changement
const result = pipe(
E.right(5),
E.map(x => x * 2),
E.flatMap(x => E.right(x + 1))
);
Migration

Rechercher & remplacer from "fp-ts/Either"from "pithos/zygos/either", etc. Toutes les fonctions fonctionnent pareil.

Fonctions disponibles

ModuleFonctions
Eitherleft, right, isLeft, isRight, map, mapLeft, flatMap, fold, match, getOrElse, orElse, fromOption, fromNullable, tryCatch, Do, bind, bindTo, apS
Taskof, map, flatMap, ap
TaskEitherleft, right, tryCatch, fromEither, fromTask, fromOption, map, mapLeft, flatMap, chain, fold, match, getOrElse, orElse, swap

safe() - Du try/catch au Result en une ligne

Permet d'encapsuler n'importe quelle fonction qui pourrait throw dans une fonction retournant un Result. C'est particulièrement utile pour les API tierces ou les fonctions natives comme JSON.parse qui communiquent les erreurs via des exceptions :

import { safe } from "pithos/zygos/safe";

const safeJsonParse = safe(JSON.parse);

const result = safeJsonParse('{"valid": true}');
// Ok({ valid: true })

const invalid = safeJsonParse('not json');
// Err(SyntaxError: ...)

Quand l'utiliser

Zygos brille dans les codebases où la gestion des erreurs doit être explicite et composable :

  • Appels APIResultAsync pour une gestion d'erreurs typée
  • Chaînes de validationResult avec andThen
  • Données optionnellesOption au lieu de null | undefined
  • Encapsuler du code unsafesafe() pour éliminer les try/catch
Kanon + Zygos

ensure() valide une donnée avec un schéma Kanon et retourne directement un Result<T, string> — prêt à chaîner avec map, andThen, etc. ensureAsync() fait la même chose pour les pipelines async avec ResultAsync, et ensurePromise() combine une Promise et la validation en un seul appel. Voir Alchimie des modules pour d'autres combinaisons.


Quand NE PAS l'utiliser

Pour des tâches en dehors de la gestion des erreurs et du flux de contrôle, d'autres modules Pithos sont plus appropriés :

BesoinUtilisez plutôt
Validation de schémasKanon
Transformation de donnéesArkhe
Fabriques d'erreurs typéesSphalma

Migrer depuis Neverthrow ou fp-ts

Depuis Neverthrow

Étape 1 : Installer

Ajoutez Pithos à votre projet. Il inclut Zygos et tous les autres modules :

npm install @pithos/core

Étape 2 : Mettre à jour les imports

// Avant
import { ok, err, Result, ResultAsync, safeTry } from "neverthrow";

// Après
import { ok, err, Result, safeTry } from "pithos/zygos/result/result";
import { ResultAsync } from "pithos/zygos/result/result-async";

Étape 3 : Exécuter vos tests

Tout votre code existant fonctionne tel quel. L'API est 100% compatible.

Étape 4 (optionnel) : Utiliser les fonctionnalités supplémentaires

Une fois migré, vous pouvez profiter des fonctionnalités spécifiques à Zygos :

// Ponts fp-ts
import { fromOption, fromEither, toEither } from "pithos/zygos/result/result";

// Try async simplifié
import { safeAsyncTry } from "pithos/zygos/result/result";

// Collecter toutes les erreurs
const allErrors = ResultAsync.combineWithAllErrors(results);

Depuis fp-ts

Étape 1 : Installer

Ajoutez Pithos à votre projet. Il inclut Zygos et tous les autres modules :

npm install @pithos/core

Étape 2 : Mettre à jour les imports

// Avant
import * as E from "fp-ts/Either";
import * as T from "fp-ts/Task";
import * as TE from "fp-ts/TaskEither";

// Après
import * as E from "pithos/zygos/either";
import * as T from "pithos/zygos/task";
import * as TE from "pithos/zygos/task-either";

Étape 3 : Exécuter vos tests

Toutes les fonctions fonctionnent pareil. pipe, map, flatMap, fold, etc. sont tous compatibles.

Pour la liste complète des méthodes Neverthrow et fp-ts supportées, voir la matrice d'interopérabilité Zygos qui documente chaque fonction à travers Result, ResultAsync, Either, Task et TaskEither.

Zygos fonctionne naturellement avec les fabriques d'erreurs typées de Sphalma : définissez des codes d'erreur structurés avec Sphalma, puis retournez-les comme des valeurs Err typées dans vos chaînes Result.


Ressources associées