carbonteq /FP

Composable Control Flow

Express “stop vs continue” rules in a linear pipeline instead of exception spaghetti.

The issue: “best-effort” rules + exceptions

Real apps rarely treat every failure the same way.

  • Invalid discount code: continue without discount.
  • Discount service down: abort and ask user to retry.
  • Receipt email fails: continue (payment already succeeded).

With exceptions, these rules often become nested try/catch and mutable state.

Bad example: nested try/catch (hard to see the policy)

type  = { : string; : number };

class  extends  {}
class  extends  {}
class  extends  {}
class  extends  {}

function (: string): number {
  if ( === "SAVE10") return 10;
  if ( === "DOWN") throw new ("discount service down");
  throw new ("invalid discount");
}

function (: number):  {
  if ( === 13) throw new ("gateway down");
  return { : "PAY-1",  };
}

function (: string): void {
  throw new ("smtp down");
}

export function (: string, : number, ?: string):  {
  try {
    if (!) throw new ("userId required");
    if (!.() ||  <= 0) throw new ("amount must be > 0");

    let  = ;

    if () {
      try {
         = .(0,  - ());
      } catch () {
        if ( instanceof ) {
           = ;
        } else {
          throw new ("Discount unavailable, retry later");
        }
      }
    }

    try {
      const  = ();

      try {
        (.);
      } catch {
        // ignored (best-effort)
      }

      return ;
    } catch {
      throw new ("Payment failed");
    }
  } catch () {
    throw ;
  }
}

The fix: make the policy explicit with Result

With @carbonteq/fp, each step returns a value that is either Ok or Err. You can keep the flow linear and make “continue vs abort” decisions local to the step.

Good example: linear pipeline (policy is obvious)

import {  } from "@carbonteq/fp";

type  = { : string; : number };

type  =
  | { : "Validation"; : string }
  | { : "DiscountUnavailable" }
  | { : "GatewayDown" };

type  =
  | { : "InvalidCode" }
  | { : "Expired" }
  | { : "ServiceDown" };

type  = { : string; : number };

function (: string, : number): <, > {
  if (!)
    return .({ : "Validation", : "userId required" });
  if (!.() ||  <= 0)
    return .({ : "Validation", : "amount must be > 0" });
  return .({ ,  });
}

function (: string): <number, > {
  if ( === "SAVE10") return .(10);
  if ( === "EXPIRED") return .({ : "Expired" });
  if ( === "DOWN") return .({ : "ServiceDown" });
  return .({ : "InvalidCode" });
}

function (: number, ?: string): <number, > {
  if (!) return .();

  return ()
    .(() => .(0,  - ))
    .(() =>
      . === "ServiceDown"
        ? .({ : "DiscountUnavailable" })
        : .() // InvalidCode/Expired: continue without discount
    );
}

function (: number): <, > {
  if ( === 13) return .({ : "GatewayDown" });
  return .({ : "PAY-1",  });
}

declare function (: string): void;

export function (: string, : number, ?: string): <, > {
  return (, )
    .(() => (., ))
    .()
    .(() => (.)); // best-effort side effect
}

On this page