• English
  • Building Objects

    When building complex objects where each field depends on successful computation, the do/bind pattern provides an elegant solution. It lets you accumulate successful results into a growing object.

    What is do?

    do creates a starting point—a Success with an empty object:

    import { 
    import Result
    Result
    } from '@praha/byethrow';
    const
    const start: Result.Result<{}, never>
    start
    =
    import Result
    Result
    .
    function do(): Result.Result<{}, never>
    export do

    Alias for succeed({}). Commonly used as a neutral base value in functional chains or monadic pipelines.

    @function@example
    import { Result } from '@praha/byethrow';
    
    const result = Result.do();
    // Result.Result<{}, never>
    @categoryCreators
    do
    ();
    // { type: 'Success', value: {} }

    What is bind?

    bind adds a new field to the accumulated object. It:

    1. Receives the current accumulated object
    2. Runs a computation that returns a Result
    3. If successful, merges the result into the object under the given key
    4. If failed, short-circuits and returns the failure
    import { 
    import Result
    Result
    } from '@praha/byethrow';
    const
    const result: Result.Result<{
        name: "Alice";
        age: 30;
    }, never>
    result
    =
    import Result
    Result
    .
    const pipe: <Result.Result<{}, never>, Result.Result<{
        name: "Alice";
    }, never>, Result.Result<{
        name: "Alice";
        age: 30;
    }, never>>(a: Result.Result<{}, never>, ab: (a: Result.Result<{}, never>) => Result.Result<{
        name: "Alice";
    }, never>, bc: (b: Result.Result<{
        name: "Alice";
    }, never>) => Result.Result<{
        name: "Alice";
        age: 30;
    }, never>) => Result.Result<{
        name: "Alice";
        age: 30;
    }, never> (+25 overloads)
    pipe
    (
    import Result
    Result
    .
    function do(): Result.Result<{}, never>
    export do

    Alias for succeed({}). Commonly used as a neutral base value in functional chains or monadic pipelines.

    @function@example
    import { Result } from '@praha/byethrow';
    
    const result = Result.do();
    // Result.Result<{}, never>
    @categoryCreators
    do
    (),
    import Result
    Result
    .
    const bind: <"name", Result.Result<{}, never>, Result.Result<"Alice", never>>(name: "name", fn: (a: {}) => Result.Result<"Alice", never>) => (result: Result.Result<{}, never>) => Result.Result<{
        name: "Alice";
    }, never> (+1 overload)
    bind
    ('name', () =>
    import Result
    Result
    .
    const succeed: <"Alice">(value: "Alice") => Result.Result<"Alice", never> (+1 overload)
    succeed
    ('Alice')),
    import Result
    Result
    .
    const bind: <"age", Result.Result<{
        name: "Alice";
    }, never>, Result.Result<30, never>>(name: "age", fn: (a: {
        name: "Alice";
    }) => Result.Result<30, never>) => (result: Result.Result<{
        name: "Alice";
    }, never>) => Result.Result<{
        name: "Alice";
        age: 30;
    }, never> (+1 overload)
    bind
    ('age', () =>
    import Result
    Result
    .
    const succeed: <30>(value: 30) => Result.Result<30, never> (+1 overload)
    succeed
    (30)),
    ); // { type: 'Success', value: { name: 'Alice', age: 30 } }

    Using Previous Values

    The power of bind is that each step can access previous values:

    import { 
    import Result
    Result
    } from '@praha/byethrow';
    const
    const result: Result.ResultAsync<{
        user: {
            id: string;
            profileId: string;
        };
        profile: {
            bio: string;
        };
    }, "UserNotFound" | "ProfileNotFound">
    result
    =
    import Result
    Result
    .
    const pipe: <Result.Result<{}, never>, Result.ResultAsync<{
        user: {
            id: string;
            profileId: string;
        };
    }, "UserNotFound">, Result.ResultAsync<{
        user: {
            id: string;
            profileId: string;
        };
        profile: {
            bio: string;
        };
    }, "UserNotFound" | "ProfileNotFound">>(a: Result.Result<{}, never>, ab: (a: Result.Result<{}, never>) => Result.ResultAsync<{
        user: {
            id: string;
            profileId: string;
        };
    }, "UserNotFound">, bc: (b: Result.ResultAsync<{
        user: {
            id: string;
            profileId: string;
        };
    }, "UserNotFound">) => Result.ResultAsync<{
        user: {
            id: string;
            profileId: string;
        };
        profile: {
            bio: string;
        };
    }, "UserNotFound" | "ProfileNotFound">) => Result.ResultAsync<...> (+25 overloads)
    pipe
    (
    import Result
    Result
    .
    function do(): Result.Result<{}, never>
    export do

    Alias for succeed({}). Commonly used as a neutral base value in functional chains or monadic pipelines.

    @function@example
    import { Result } from '@praha/byethrow';
    
    const result = Result.do();
    // Result.Result<{}, never>
    @categoryCreators
    do
    (),
    import Result
    Result
    .
    const bind: <"user", Result.Result<{}, never>, Result.ResultAsync<{
        id: string;
        profileId: string;
    }, "UserNotFound">>(name: "user", fn: (a: {}) => Result.ResultAsync<{
        id: string;
        profileId: string;
    }, "UserNotFound">) => (result: Result.Result<{}, never>) => Result.ResultAsync<{
        user: {
            id: string;
            profileId: string;
        };
    }, "UserNotFound"> (+1 overload)
    bind
    ('user', () =>
    const fetchUser: (userId: string) => Result.ResultAsync<{
        id: string;
        profileId: string;
    }, "UserNotFound">
    fetchUser
    ('user-123')),
    import Result
    Result
    .
    const bind: <"profile", Result.ResultAsync<{
        user: {
            id: string;
            profileId: string;
        };
    }, "UserNotFound">, Result.ResultAsync<{
        bio: string;
    }, "ProfileNotFound">>(name: "profile", fn: (a: {
        user: {
            id: string;
            profileId: string;
        };
    }) => Result.ResultAsync<{
        bio: string;
    }, "ProfileNotFound">) => (result: Result.ResultAsync<{
        user: {
            id: string;
            profileId: string;
        };
    }, "UserNotFound">) => Result.ResultAsync<{
        user: {
            id: string;
            profileId: string;
        };
        profile: {
            bio: string;
        };
    }, "UserNotFound" | "ProfileNotFound"> (+1 overload)
    bind
    ('profile', ({
    user: {
        id: string;
        profileId: string;
    }
    user
    }) =>
    const fetchProfile: (profileId: string) => Result.ResultAsync<{
        bio: string;
    }, "ProfileNotFound">
    fetchProfile
    (
    user: {
        id: string;
        profileId: string;
    }
    user
    .
    profileId: string
    profileId
    )), // Uses user!
    );

    Failure Short-Circuits

    If any bind fails, subsequent binds are skipped:

    import { 
    import Result
    Result
    } from '@praha/byethrow';
    const
    const result: Result.Result<{
        a: 1;
        b: never;
        c: 3;
        d: 4;
    }, "Error!">
    result
    =
    import Result
    Result
    .
    const pipe: <Result.Result<{}, never>, Result.Result<{
        a: 1;
    }, never>, Result.Result<{
        a: 1;
        b: never;
    }, "Error!">, Result.Result<{
        a: 1;
        b: never;
        c: 3;
    }, "Error!">, Result.Result<{
        a: 1;
        b: never;
        c: 3;
        d: 4;
    }, "Error!">>(a: Result.Result<{}, never>, ab: (a: Result.Result<{}, never>) => Result.Result<{
        a: 1;
    }, never>, bc: (b: Result.Result<{
        a: 1;
    }, never>) => Result.Result<{
        a: 1;
        b: never;
    }, "Error!">, cd: (c: Result.Result<{
        a: 1;
        b: never;
    }, "Error!">) => Result.Result<{
        a: 1;
        b: never;
        c: 3;
    }, "Error!">, de: (d: Result.Result<...>) => Result.Result<...>) => Result.Result<...> (+25 overloads)
    pipe
    (
    import Result
    Result
    .
    function do(): Result.Result<{}, never>
    export do

    Alias for succeed({}). Commonly used as a neutral base value in functional chains or monadic pipelines.

    @function@example
    import { Result } from '@praha/byethrow';
    
    const result = Result.do();
    // Result.Result<{}, never>
    @categoryCreators
    do
    (),
    import Result
    Result
    .
    const bind: <"a", Result.Result<{}, never>, Result.Result<1, never>>(name: "a", fn: (a: {}) => Result.Result<1, never>) => (result: Result.Result<{}, never>) => Result.Result<{
        a: 1;
    }, never> (+1 overload)
    bind
    ('a', () =>
    import Result
    Result
    .
    const succeed: <1>(value: 1) => Result.Result<1, never> (+1 overload)
    succeed
    (1)),
    import Result
    Result
    .
    const bind: <"b", Result.Result<{
        a: 1;
    }, never>, Result.Result<never, "Error!">>(name: "b", fn: (a: {
        a: 1;
    }) => Result.Result<never, "Error!">) => (result: Result.Result<{
        a: 1;
    }, never>) => Result.Result<{
        a: 1;
        b: never;
    }, "Error!"> (+1 overload)
    bind
    ('b', () =>
    import Result
    Result
    .
    const fail: <"Error!">(error: "Error!") => Result.Result<never, "Error!"> (+1 overload)
    fail
    ('Error!')), // Fails here
    import Result
    Result
    .
    const bind: <"c", Result.Result<{
        a: 1;
        b: never;
    }, "Error!">, Result.Result<3, never>>(name: "c", fn: (a: {
        a: 1;
        b: never;
    }) => Result.Result<3, never>) => (result: Result.Result<{
        a: 1;
        b: never;
    }, "Error!">) => Result.Result<{
        a: 1;
        b: never;
        c: 3;
    }, "Error!"> (+1 overload)
    bind
    ('c', () =>
    import Result
    Result
    .
    const succeed: <3>(value: 3) => Result.Result<3, never> (+1 overload)
    succeed
    (3)), // Skipped
    import Result
    Result
    .
    const bind: <"d", Result.Result<{
        a: 1;
        b: never;
        c: 3;
    }, "Error!">, Result.Result<4, never>>(name: "d", fn: (a: {
        a: 1;
        b: never;
        c: 3;
    }) => Result.Result<4, never>) => (result: Result.Result<{
        a: 1;
        b: never;
        c: 3;
    }, "Error!">) => Result.Result<{
        a: 1;
        b: never;
        c: 3;
        d: 4;
    }, "Error!"> (+1 overload)
    bind
    ('d', () =>
    import Result
    Result
    .
    const succeed: <4>(value: 4) => Result.Result<4, never> (+1 overload)
    succeed
    (4)), // Skipped
    ); // { type: 'Failure', error: 'Error!' }

    When you need to fetch multiple related resources and combine them:

    import { 
    import Result
    Result
    } from '@praha/byethrow';
    type
    type OrderDetails = {
        orderId: string;
        customerName: string;
        products: {
            id: string;
            name: string;
            price: number;
        }[];
        totalPrice: number;
    }
    OrderDetails
    = {
    orderId: string
    orderId
    : string;
    customerName: string
    customerName
    : string;
    products: {
        id: string;
        name: string;
        price: number;
    }[]
    products
    : {
    id: string
    id
    : string;
    name: string
    name
    : string;
    price: number
    price
    : number }[];
    totalPrice: number
    totalPrice
    : number;
    }; const
    const getOrderDetails: (orderId: string) => Result.ResultAsync<OrderDetails, "OrderNotFound" | "UserNotFound" | "ProductNotFound">
    getOrderDetails
    = (
    orderId: string
    orderId
    : 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 OrderDetails = {
        orderId: string;
        customerName: string;
        products: {
            id: string;
            name: string;
            price: number;
        }[];
        totalPrice: number;
    }
    OrderDetails
    , 'OrderNotFound' | 'UserNotFound' | 'ProductNotFound'> => {
    return
    import Result
    Result
    .
    const pipe: <Result.Result<{}, never>, Result.ResultAsync<{
        order: {
            id: string;
            userId: string;
            productIds: string[];
        };
    }, "OrderNotFound">, Result.ResultAsync<{
        order: {
            id: string;
            userId: string;
            productIds: string[];
        };
        user: {
            id: string;
            name: string;
        };
    }, "OrderNotFound" | "UserNotFound">, Result.ResultAsync<{
        order: {
            id: string;
            userId: string;
            productIds: string[];
        };
        user: {
            id: string;
            name: string;
        };
        products: {
            id: string;
            name: string;
            price: number;
        }[];
    }, "OrderNotFound" | "UserNotFound" | "ProductNotFound">, Result.ResultAsync<{
        readonly orderId: string;
        readonly customerName: string;
        readonly products: {
            id: string;
            name: string;
            price: number;
        }[];
        readonly totalPrice: number;
    }, "OrderNotFound" | ... 1 more ... | "ProductNotFound">>(a: Result.Result<...>, ab: (a: Result.Result<...>) => Result.ResultAsync<...>, bc: (b: Result.ResultAsync<...>) => Result.ResultAsync<...>, cd: (c: Result.ResultAsync<...>) => Result.ResultAsync<...>, de: (d: Result.ResultAsync<...>) => Result.ResultAsync<...>) => Result.ResultAsync<...> (+25 overloads)
    pipe
    (
    import Result
    Result
    .
    function do(): Result.Result<{}, never>
    export do

    Alias for succeed({}). Commonly used as a neutral base value in functional chains or monadic pipelines.

    @function@example
    import { Result } from '@praha/byethrow';
    
    const result = Result.do();
    // Result.Result<{}, never>
    @categoryCreators
    do
    (),
    import Result
    Result
    .
    const bind: <"order", Result.Result<{}, never>, Result.ResultAsync<{
        id: string;
        userId: string;
        productIds: string[];
    }, "OrderNotFound">>(name: "order", fn: (a: {}) => Result.ResultAsync<{
        id: string;
        userId: string;
        productIds: string[];
    }, "OrderNotFound">) => (result: Result.Result<{}, never>) => Result.ResultAsync<{
        order: {
            id: string;
            userId: string;
            productIds: string[];
        };
    }, "OrderNotFound"> (+1 overload)
    bind
    ('order', () =>
    const fetchOrder: (orderId: string) => Result.ResultAsync<{
        id: string;
        userId: string;
        productIds: string[];
    }, "OrderNotFound">
    fetchOrder
    (
    orderId: string
    orderId
    )),
    import Result
    Result
    .
    const bind: <"user", Result.ResultAsync<{
        order: {
            id: string;
            userId: string;
            productIds: string[];
        };
    }, "OrderNotFound">, Result.ResultAsync<{
        id: string;
        name: string;
    }, "UserNotFound">>(name: "user", fn: (a: {
        order: {
            id: string;
            userId: string;
            productIds: string[];
        };
    }) => Result.ResultAsync<{
        id: string;
        name: string;
    }, "UserNotFound">) => (result: Result.ResultAsync<{
        order: {
            id: string;
            userId: string;
            productIds: string[];
        };
    }, "OrderNotFound">) => Result.ResultAsync<{
        order: {
            id: string;
            userId: string;
            productIds: string[];
        };
        user: {
            id: string;
            name: string;
        };
    }, "OrderNotFound" | "UserNotFound"> (+1 overload)
    bind
    ('user', ({
    order: {
        id: string;
        userId: string;
        productIds: string[];
    }
    order
    }) =>
    const fetchUser: (userId: string) => Result.ResultAsync<{
        id: string;
        name: string;
    }, "UserNotFound">
    fetchUser
    (
    order: {
        id: string;
        userId: string;
        productIds: string[];
    }
    order
    .
    userId: string
    userId
    )),
    import Result
    Result
    .
    const bind: <"products", Result.ResultAsync<{
        order: {
            id: string;
            userId: string;
            productIds: string[];
        };
        user: {
            id: string;
            name: string;
        };
    }, "OrderNotFound" | "UserNotFound">, Result.ResultAsync<{
        id: string;
        name: string;
        price: number;
    }[], "ProductNotFound">>(name: "products", fn: (a: {
        order: {
            id: string;
            userId: string;
            productIds: string[];
        };
        user: {
            id: string;
            name: string;
        };
    }) => Result.ResultAsync<{
        id: string;
        name: string;
        price: number;
    }[], "ProductNotFound">) => (result: Result.ResultAsync<{
        order: {
            id: string;
            userId: string;
            productIds: string[];
        };
        user: {
            id: string;
            name: string;
        };
    }, "OrderNotFound" | "UserNotFound">) => Result.ResultAsync<...> (+1 overload)
    bind
    ('products', ({
    order: {
        id: string;
        userId: string;
        productIds: string[];
    }
    order
    }) =>
    const fetchProducts: (productIds: string[]) => Result.ResultAsync<{
        id: string;
        name: string;
        price: number;
    }[], "ProductNotFound">
    fetchProducts
    (
    order: {
        id: string;
        userId: string;
        productIds: string[];
    }
    order
    .
    productIds: string[]
    productIds
    )),
    import Result
    Result
    .
    const map: <Result.ResultAsync<{
        order: {
            id: string;
            userId: string;
            productIds: string[];
        };
        user: {
            id: string;
            name: string;
        };
        products: {
            id: string;
            name: string;
            price: number;
        }[];
    }, "OrderNotFound" | "UserNotFound" | "ProductNotFound">, {
        readonly orderId: string;
        readonly customerName: string;
        readonly products: {
            id: string;
            name: string;
            price: number;
        }[];
        readonly totalPrice: number;
    }>(fn: (a: {
        order: {
            id: string;
            userId: string;
            productIds: string[];
        };
        user: {
            id: string;
            name: string;
        };
        products: {
            id: string;
            name: string;
            price: number;
        }[];
    }) => {
        readonly orderId: string;
        readonly customerName: string;
        readonly products: {
            id: string;
            name: string;
            price: number;
        }[];
        readonly totalPrice: number;
    }) => (result: Result.ResultAsync<...>) => Result.ResultAsync<...> (+1 overload)
    map
    (({
    order: {
        id: string;
        userId: string;
        productIds: string[];
    }
    order
    ,
    user: {
        id: string;
        name: string;
    }
    user
    ,
    products: {
        id: string;
        name: string;
        price: number;
    }[]
    products
    }) => ({
    orderId: string
    orderId
    :
    order: {
        id: string;
        userId: string;
        productIds: string[];
    }
    order
    .
    id: string
    id
    ,
    customerName: string
    customerName
    :
    user: {
        id: string;
        name: string;
    }
    user
    .
    name: string
    name
    ,
    products: {
        id: string;
        name: string;
        price: number;
    }[]
    products
    ,
    totalPrice: number
    totalPrice
    :
    products: {
        id: string;
        name: string;
        price: number;
    }[]
    products
    .
    Array<{ id: string; name: string; price: number; }>.reduce<number>(callbackfn: (previousValue: number, currentValue: {
        id: string;
        name: string;
        price: number;
    }, currentIndex: number, array: {
        id: string;
        name: string;
        price: number;
    }[]) => number, initialValue: number): number (+2 overloads)

    Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.

    @paramcallbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.@paraminitialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
    reduce
    ((
    sum: number
    sum
    ,
    p: {
        id: string;
        name: string;
        price: number;
    }
    p
    ) =>
    sum: number
    sum
    +
    p: {
        id: string;
        name: string;
        price: number;
    }
    p
    .
    price: number
    price
    , 0),
    })), ); }; // Uses order data to fetch user and products, then combines everything

    References

    FunctionPurpose
    do()Start with Success<{}>
    bind(name, fn)Add field to accumulated object