• English
  • Pipe Basics

    The pipe function is the heart of @praha/byethrow. It allows you to chain multiple operations together in a readable, left-to-right manner.

    What is pipe?

    pipe takes an initial value and passes it through a series of functions, where each function receives the output of the previous one:

    import { 
    import Result
    Result
    } from '@praha/byethrow';
    const
    const result: string
    result
    =
    import Result
    Result
    .
    const pipe: <number, number, number, string>(a: number, ab: (a: number) => number, bc: (b: number) => number, cd: (c: number) => string) => string (+25 overloads)
    pipe
    (
    5, // Start with 5 (
    x: number
    x
    ) =>
    x: number
    x
    + 1, // 5 + 1 = 6
    (
    x: number
    x
    ) =>
    x: number
    x
    * 2, // 6 * 2 = 12
    (
    x: number
    x
    ) => `Result: ${
    x: number
    x
    }` // 'Result: 12'
    ); // 'Result: 12&

    Why Use pipe?

    Without pipe

    Nested function calls are hard to read:

    // ❌ Hard to read - inside out
    const 
    const result: string
    result
    =
    const format: (x: number) => string
    format
    (
    const multiply: (a: number, b: number) => number
    multiply
    (
    const add: (a: number, b: number) => number
    add
    (5, 1), 2));

    Or intermediate variables are verbose:

    // ❌ Too many variables
    const 
    const step1: number
    step1
    =
    const add: (a: number, b: number) => number
    add
    (5, 1);
    const
    const step2: number
    step2
    =
    const multiply: (a: number, b: number) => number
    multiply
    (
    const step1: number
    step1
    , 2);
    const
    const result: string
    result
    =
    const format: (x: number) => string
    format
    (
    const step2: number
    step2
    );

    With pipe

    import { 
    import Result
    Result
    } from '@praha/byethrow';
    // ✅ Clear, left-to-right flow const
    const result: string
    result
    =
    import Result
    Result
    .
    const pipe: <number, number, number, string>(a: number, ab: (a: number) => number, bc: (b: number) => number, cd: (c: number) => string) => string (+25 overloads)
    pipe
    (
    5, (
    x: number
    x
    ) =>
    const add: (a: number, b: number) => number
    add
    (
    x: number
    x
    , 1),
    (
    x: number
    x
    ) =>
    const multiply: (a: number, b: number) => number
    multiply
    (
    x: number
    x
    , 2),
    const format: (x: number) => string
    format
    );

    Using pipe with Results

    The real power of pipe comes when working with Results. All the transformation functions in @praha/byethrow are designed to work with pipe:

    import { 
    import Result
    Result
    } from '@praha/byethrow';
    const
    const result: Result.Result<number, never>
    result
    =
    import Result
    Result
    .
    const pipe: <Result.Result<10, never>, Result.Result<number, never>, Result.Result<number, never>>(a: Result.Result<10, never>, ab: (a: Result.Result<10, never>) => Result.Result<number, never>, bc: (b: Result.Result<number, never>) => Result.Result<number, never>) => Result.Result<number, never> (+25 overloads)
    pipe
    (
    import Result
    Result
    .
    const succeed: <10>(value: 10) => Result.Result<10, never> (+1 overload)
    succeed
    (10),
    import Result
    Result
    .
    const map: <Result.Result<10, never>, number>(fn: (a: 10) => number) => (result: Result.Result<10, never>) => Result.Result<number, never> (+1 overload)
    map
    ((
    x: 10
    x
    ) =>
    x: 10
    x
    * 2),
    import Result
    Result
    .
    const map: <Result.Result<number, never>, number>(fn: (a: number) => number) => (result: Result.Result<number, never>) => Result.Result<number, never> (+1 overload)
    map
    ((
    x: number
    x
    ) =>
    x: number
    x
    + 5),
    ); // { type: 'Success', value: 25 }

    How pipe Handles Async

    When any function in the pipe returns a Promise, the result becomes a Promise:

    import { 
    import Result
    Result
    } from '@praha/byethrow';
    type
    type User = {
        id: string;
        name: string;
    }
    User
    = {
    id: string
    id
    : string;
    name: string
    name
    : string; };
    const
    const fetchUser: (id: string) => Result.ResultAsync<User, string>
    fetchUser
    = async (
    id: string
    id
    : string):
    import Result
    Result
    .
    type ResultAsync<T, E> = Promise<Result.Result<T, E>>

    An asynchronous variant of Result , wrapped in a Promise.

    @typeParamT - The type of the Success value.@typeParamE - The type of the Failure value.@example
    import { Result } from '@praha/byethrow';
    
    const fetchData = async (): Result.ResultAsync<string, Error> => {
      try {
        const data = await fetch('...');
        return { type: 'Success', value: await data.text() };
      } catch (err) {
        return { type: 'Failure', error: err as Error };
      }
    };
    @categoryCore Types
    ResultAsync
    <
    type User = {
        id: string;
        name: string;
    }
    User
    , string> => {
    const
    const response: Response
    response
    = await
    function fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>
    fetch
    (`/api/users/${
    id: string
    id
    }`);
    if (!
    const response: Response
    response
    .
    Response.ok: boolean

    The ok read-only property of the Response interface contains a Boolean stating whether the response was successful (status in the range 200-299) or not.

    MDN Reference

    ok
    ) {
    return
    import Result
    Result
    .
    const fail: <"User not found">(error: "User not found") => Result.Result<never, "User not found"> (+1 overload)
    fail
    ('User not found');
    } const
    const user: any
    user
    = await
    const response: Response
    response
    .
    Body.json(): Promise<any>
    json
    ();
    return
    import Result
    Result
    .
    const succeed: <any>(value: any) => Result.Result<any, never> (+1 overload)
    succeed
    (
    const user: any
    user
    );
    }; const
    const result: Result.Result<string, string>
    result
    = await
    import Result
    Result
    .
    const pipe: <Result.Result<"user-123", never>, Result.ResultAsync<User, string>, Result.ResultAsync<string, string>>(a: Result.Result<"user-123", never>, ab: (a: Result.Result<"user-123", never>) => Result.ResultAsync<User, string>, bc: (b: Result.ResultAsync<User, string>) => Result.ResultAsync<string, string>) => Result.ResultAsync<string, string> (+25 overloads)
    pipe
    (
    import Result
    Result
    .
    const succeed: <"user-123">(value: "user-123") => Result.Result<"user-123", never> (+1 overload)
    succeed
    ('user-123'),
    import Result
    Result
    .
    const andThen: <Result.Result<"user-123", never>, Result.ResultAsync<User, string>>(fn: (a: "user-123") => Result.ResultAsync<User, string>) => (result: Result.Result<"user-123", never>) => Result.ResultAsync<User, string> (+1 overload)
    andThen
    (
    const fetchUser: (id: string) => Result.ResultAsync<User, string>
    fetchUser
    ),
    import Result
    Result
    .
    const map: <Result.ResultAsync<User, string>, string>(fn: (a: User) => string) => (result: Result.ResultAsync<User, string>) => Result.ResultAsync<string, string> (+1 overload)
    map
    ((
    user: User
    user
    ) =>
    user: User
    user
    .
    name: string
    name
    ),
    );