#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(): import("/home/runner/work/byethrow/byethrow/packages/byethrow/dist/esm/result").Result<{}, never>
export do
Alias for succeed({}). Commonly used as a neutral base value in functional chains or monadic pipelines.
@function@exampleimport { 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:
- Receives the current accumulated object
- Runs a computation that returns a Result
- If successful, merges the result into the object under the given key
- 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(): import("/home/runner/work/byethrow/byethrow/packages/byethrow/dist/esm/result").Result<{}, never>
export do
Alias for succeed({}). Commonly used as a neutral base value in functional chains or monadic pipelines.
@function@exampleimport { 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(): import("/home/runner/work/byethrow/byethrow/packages/byethrow/dist/esm/result").Result<{}, never>
export do
Alias for succeed({}). Commonly used as a neutral base value in functional chains or monadic pipelines.
@function@exampleimport { 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(): import("/home/runner/work/byethrow/byethrow/packages/byethrow/dist/esm/result").Result<{}, never>
export do
Alias for succeed({}). Commonly used as a neutral base value in functional chains or monadic pipelines.
@function@exampleimport { 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!' }#Example: Fetching Related Data
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.@exampleimport { 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(): import("/home/runner/work/byethrow/byethrow/packages/byethrow/dist/esm/result").Result<{}, never>
export do
Alias for succeed({}). Commonly used as a neutral base value in functional chains or monadic pipelines.
@function@exampleimport { 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
| Function | Purpose |
|---|---|
| do() | Start with Success<{}> |
| bind(name, fn) | Add field to accumulated object |
