carbonteq /FP

Building Pipelines

Learn how to compose complex logic into readable, safe pipelines using Result and Option.

Pipelines are the heart of functional programming. Instead of nesting if statements or using try-catch for control flow, you chain operations together. This creates a "railway" where data flows through the happy path, but automatically switches to the error track the moment something goes wrong.

The Railway Mental Model

In a pipeline, every step either:

  • Stays on the track: Returns Ok(value) or Some(value), passing it to the next function.
  • Derails: Returns Err(error) or None, bypassing all subsequent steps until it hits a handler.

This approach ensures that your core business logic isn't buried under error checks.

Composing a Business Flow

A synchronous pipeline for in-memory validation and transformation:

import {  } from "@carbonteq/fp";

type  = { : string; : number; : string };
type  = { : string; : number };

const  = 29.99;
const  = 100;

const  = (: ): <, string> =>
  !. ? .("Product ID required")
  : . <= 0 ? .("Quantity must be positive")
  : !. ? .("User ID required")
  : .();

const  = (: ): <, string> =>
  . >  ? .("Insufficient stock") : .();

export function (: ): <, string | string[]> {
  return .()
    .([, ])
    .(() => ({
      : `ORD-${.()}`,
      : . * ,
    }))
    .(() => .(`Processed: ${.}`));
}

An asynchronous pipeline with database/API calls. Use .toPromise() at the end:

import {  } from "@carbonteq/fp";

type  = { : string; : string };
type  = { : string; : string };

declare function (: string): <boolean>;
declare function (: string): <string>;
declare function (: string, : string): <>;

async function (: ): <<, string>> {
  const  = await (.);
  return  ? .("Email taken") : .();
}

async function (: ): <<, string>> {
  return .(
    async () => {
      const  = await (.);
      return (., );
    },
    () => "Failed to create user",
  ).();
}

export async function (: ): <<, string | string[]>> {
  return .()
    .([
      () => (..("@") ? .() : .("Invalid email")),
      () => (.. >= 8 ? .() : .("Password too short")),
    ])
    .()
    .()
    .(() => .(`Registered: ${.}`))
    .();
}

Validation with Error Accumulation

The .validate() operator runs multiple validators and collects all errors:

import {  } from "@carbonteq/fp";

const  = (: string) =>
  . >= 8 ? .() : .("Min 8 characters");

const  = (: string) =>
  /\d/.() ? .() : .("Must contain a number");

const  = (: string) =>
  /[!@#$%^&*]/.() ? .() : .("Needs special character");

// Collects ALL failing rules, not just the first
const  = (: string) =>
  .().([, , ]);

const  = ("abc");
// Err(["Min 8 characters", "Must contain a number", "Needs special character"])

const  = ("MyP@ssw0rd!");
// Ok("MyP@ssw0rd!")

Error Transformation

Keep errors structured internally, format them at the edge.

Transform with mapErr

import {  } from "@carbonteq/fp";

type  = { : string; : string };

const  = (: string):  => ({
  : "VALIDATION_ERROR",
  : ,
});

const  = .("email is required").();
// Err({ code: "VALIDATION_ERROR", message: "email is required" })

Enrich with mapBoth

import {  } from "@carbonteq/fp";

type <> =  & { : number };

const  = .({ : "u1" }).(
  () => ({ ..., : .() }),
  () => ({ : (), : .() }),
);

Debugging with tap and tapErr

Add logging without breaking the pipeline:

import {  } from "@carbonteq/fp";

type  = { : string; : string[] };

declare function (: ): <, string>;
declare function (: ): <, string>;

const  = (: ) =>
  .()
    .(() => .(`[START] Processing ${.}`))
    .()
    .(() => .(`[VALID] ${.}`))
    .()
    .(() => .(`[CHARGED] ${.}`))
    .(() => .(`[FAILED] ${}`));

Fallback Chains with orElse

Recover from errors with fallback strategies:

import {  } from "@carbonteq/fp";

type  = { : string };

declare function (): <, Error>;
declare function (): <, Error>;

const :  = { : "https://api.example.com" };

const  = () =>
  ()
    .()
    .(() => .());

Combining Multiple Results

Use Result.all when all must succeed, or Result.any for first-success:

import {  } from "@carbonteq/fp";

declare function (: string): <{ : string }, string>;
declare function (: string): <{ : number }, string>;

// All must succeed
const  = (: string, : string) =>
  .((), ())
    .(([, ]) => ({ : ., : . }));

// First success wins
declare function (): <string, Error>;
declare function (): <string, Error>;

const  = () => .((), ());

Next Steps

On this page