#関数を合成する
andThen と orElse 関数を使うと、それ自体が Result を返す可能性のある計算を連結できます。
値を変換するだけの map や mapError とは異なり、これらの関数は各ステップが成功または失敗する可能性のある順次処理を可能にします。
#andThen - 成功時の計算を連結
andThen 関数は成功値を使って次の計算を連結します。
元の Result が Failure の場合は、変更されずにそのまま返されます。
それ以外の場合、提供された関数が呼び出され、その結果がそのまま返されます。
#基本的な使い方
import { import Result Result } from '@praha/byethrow';
const const result: Result.Result<number, never> result = import Result Result .const pipe: <Result.Result<3, never>, Result.Result<number, never>>(a: Result.Result<3, never>, ab: (a: Result.Result<3, never>) => Result.Result<number, never>) => Result.Result<number, never> (+25 overloads) pipe (
import Result Result .const succeed: <3>(value: 3) => Result.Result<3, never> (+1 overload) succeed (3),
import Result Result .const andThen: <Result.Result<3, never>, Result.Result<number, never>>(fn: (a: 3) => Result.Result<number, never>) => (result: Result.Result<3, never>) => Result.Result<number, never> (+1 overload) andThen ((value: 3 value ) => import Result Result .const succeed: <number>(value: number) => Result.Result<number, never> (+1 overload) succeed (value: 3 value * 2)),
);
// { type: 'Success', value: 6 }#入力が Failure の場合
入力が Failure の場合、andThen は何もせず、Failure をそのまま返します。
import { import Result Result } from '@praha/byethrow';
const const result: Result.Result<number, "error"> result = import Result Result .const pipe: <Result.Result<never, "error">, Result.Result<number, "error">>(a: Result.Result<never, "error">, ab: (a: Result.Result<never, "error">) => Result.Result<number, "error">) => Result.Result<number, "error"> (+25 overloads) pipe (
import Result Result .const fail: <"error">(error: "error") => Result.Result<never, "error"> (+1 overload) fail ('error'),
import Result Result .const andThen: <Result.Result<never, "error">, Result.Result<number, never>>(fn: (a: never) => Result.Result<number, never>) => (result: Result.Result<never, "error">) => Result.Result<number, "error"> (+1 overload) andThen ((value: never value ) => import Result Result .const succeed: <number>(value: number) => Result.Result<number, never> (+1 overload) succeed (value: never value * 2)),
);
// { type: 'Failure', error: 'error' }#関数が Failure を返す場合
連結された関数は Failure を返すことができ、それはパイプラインを通じて伝播します。
import { import Result Result } from '@praha/byethrow';
const const result: Result.Result<never, string> result = import Result Result .const pipe: <Result.Result<3, never>, Result.Result<never, string>>(a: Result.Result<3, never>, ab: (a: Result.Result<3, never>) => Result.Result<never, string>) => Result.Result<never, string> (+25 overloads) pipe (
import Result Result .const succeed: <3>(value: 3) => Result.Result<3, never> (+1 overload) succeed (3),
import Result Result .const andThen: <Result.Result<3, never>, Result.Result<never, string>>(fn: (a: 3) => Result.Result<never, string>) => (result: Result.Result<3, never>) => Result.Result<never, string> (+1 overload) andThen ((value: 3 value ) => import Result Result .const fail: <string>(error: string) => Result.Result<never, string> (+1 overload) fail ('error: ' + value: 3 value )),
);
// { type: 'Failure', error: 'error: 3' }#例:順次バリデーション
よくある使い方として、複数のバリデーションや処理ステップを連結する例があります。
import { import Result Result } from '@praha/byethrow';
type type User = {
id: string;
name: string;
email: string;
}
User = { id: string id : string; name: string name : string; email: string email : string };
declare const const findUserById: (id: string) => Result.ResultAsync<User, "NotFound"> findUserById : (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.@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 User = {
id: string;
name: string;
email: string;
}
User , 'NotFound'>;
declare const const validateEmail: (user: User) => Result.Result<User, "InvalidEmail"> validateEmail : (user: User user : type User = {
id: string;
name: string;
email: string;
}
User ) => import Result Result .type Result<T, E> = Result.Success<T> | Result.Failure<E>A union type representing either a success or a failure.
@typeParamT - The type of the Success value.@typeParamE - The type of the Failure value.@exampleimport { Result } from '@praha/byethrow';
const doSomething = (): Result.Result<number, string> => {
return Math.random() > 0.5
? { type: 'Success', value: 10 }
: { type: 'Failure', error: 'Oops' };
};
@categoryCore Types Result <type User = {
id: string;
name: string;
email: string;
}
User , 'InvalidEmail'>;
declare const const saveUser: (user: User) => Result.ResultAsync<User, "SaveFailed"> saveUser : (user: User user : type User = {
id: string;
name: string;
email: string;
}
User ) => 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 User = {
id: string;
name: string;
email: string;
}
User , 'SaveFailed'>;
const const result: Result.Result<User, "NotFound" | "InvalidEmail" | "SaveFailed"> result = await import Result Result .const pipe: <Result.Result<"user-123", never>, Result.ResultAsync<User, "NotFound">, Result.ResultAsync<User, "NotFound" | "InvalidEmail">, Result.ResultAsync<User, "NotFound" | "InvalidEmail" | "SaveFailed">>(a: Result.Result<"user-123", never>, ab: (a: Result.Result<"user-123", never>) => Result.ResultAsync<User, "NotFound">, bc: (b: Result.ResultAsync<User, "NotFound">) => Result.ResultAsync<User, "NotFound" | "InvalidEmail">, cd: (c: Result.ResultAsync<...>) => Result.ResultAsync<...>) => Result.ResultAsync<...> (+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, "NotFound">>(fn: (a: "user-123") => Result.ResultAsync<User, "NotFound">) => (result: Result.Result<"user-123", never>) => Result.ResultAsync<User, "NotFound"> (+1 overload) andThen (const findUserById: (id: string) => Result.ResultAsync<User, "NotFound"> findUserById ),
import Result Result .const andThen: <Result.ResultAsync<User, "NotFound">, Result.Result<User, "InvalidEmail">>(fn: (a: User) => Result.Result<User, "InvalidEmail">) => (result: Result.ResultAsync<User, "NotFound">) => Result.ResultAsync<User, "NotFound" | "InvalidEmail"> (+1 overload) andThen (const validateEmail: (user: User) => Result.Result<User, "InvalidEmail"> validateEmail ),
import Result Result .const andThen: <Result.ResultAsync<User, "NotFound" | "InvalidEmail">, Result.ResultAsync<User, "SaveFailed">>(fn: (a: User) => Result.ResultAsync<User, "SaveFailed">) => (result: Result.ResultAsync<User, "NotFound" | "InvalidEmail">) => Result.ResultAsync<User, "NotFound" | "InvalidEmail" | "SaveFailed"> (+1 overload) andThen (const saveUser: (user: User) => Result.ResultAsync<User, "SaveFailed"> saveUser ),
);
// いずれかのステップが失敗すると、パイプラインはそのエラーでショートサーキットする#orElse - 失敗からの回復
orElse 関数はエラー値を使って次の計算を連結します。
元の Result が Success の場合は、変更されずにそのまま返されます。
それ以外の場合、提供された関数が呼び出され、その結果がそのまま返されます。
これはエラー回復に便利で、フォールバック値を提供したり、異なる戦略でリトライしたりできます。
#基本的な使い方
import { import Result Result } from '@praha/byethrow';
const const result: Result.Result<0 | 42, never> result = import Result Result .const pipe: <Result.Result<42, never>, Result.Result<0 | 42, never>>(a: Result.Result<42, never>, ab: (a: Result.Result<42, never>) => Result.Result<0 | 42, never>) => Result.Result<0 | 42, never> (+25 overloads) pipe (
import Result Result .const succeed: <42>(value: 42) => Result.Result<42, never> (+1 overload) succeed (42),
import Result Result .const orElse: <Result.Result<42, never>, Result.Result<0, never>>(fn: (a: never) => Result.Result<0, never>) => (result: Result.Result<42, never>) => Result.Result<0 | 42, never> (+1 overload) orElse ((error: never error ) => import Result Result .const succeed: <0>(value: 0) => Result.Result<0, never> (+1 overload) succeed (0)),
);
// { type: 'Success', value: 42 }#入力が Failure の場合
入力が Failure の場合、orElse は回復関数を実行します。
import { import Result Result } from '@praha/byethrow';
const const result: Result.Result<"default value", never> result = import Result Result .const pipe: <Result.Result<never, "original error">, Result.Result<"default value", never>>(a: Result.Result<never, "original error">, ab: (a: Result.Result<never, "original error">) => Result.Result<"default value", never>) => Result.Result<"default value", never> (+25 overloads) pipe (
import Result Result .const fail: <"original error">(error: "original error") => Result.Result<never, "original error"> (+1 overload) fail ('original error'),
import Result Result .const orElse: <Result.Result<never, "original error">, Result.Result<"default value", never>>(fn: (a: "original error") => Result.Result<"default value", never>) => (result: Result.Result<never, "original error">) => Result.Result<"default value", never> (+1 overload) orElse ((error: "original error" error ) => import Result Result .const succeed: <"default value">(value: "default value") => Result.Result<"default value", never> (+1 overload) succeed ('default value')),
);
// { type: 'Success', value: 'default value' }#回復関数が Failure を返す場合
回復関数も Failure を返すことができます。
import { import Result Result } from '@praha/byethrow';
const const result: Result.Result<never, string> result = import Result Result .const pipe: <Result.Result<never, "original error">, Result.Result<never, string>>(a: Result.Result<never, "original error">, ab: (a: Result.Result<never, "original error">) => Result.Result<never, string>) => Result.Result<never, string> (+25 overloads) pipe (
import Result Result .const fail: <"original error">(error: "original error") => Result.Result<never, "original error"> (+1 overload) fail ('original error'),
import Result Result .const orElse: <Result.Result<never, "original error">, Result.Result<never, string>>(fn: (a: "original error") => Result.Result<never, string>) => (result: Result.Result<never, "original error">) => Result.Result<never, string> (+1 overload) orElse ((error: "original error" error ) => import Result Result .const fail: <string>(error: string) => Result.Result<never, string> (+1 overload) fail ('new error: ' + error: "original error" error )),
);
// { type: 'Failure', error: 'new error: original error' }#例:フォールバック戦略
よくあるパターンとして、フォールバック戦略の実装があります。
import { import Result Result } from '@praha/byethrow';
type type Config = {
apiUrl: string;
timeout: number;
}
Config = { apiUrl: string apiUrl : string; timeout: number timeout : number };
declare const const loadConfigFromFile: () => Result.ResultAsync<Config, "FileNotFound"> loadConfigFromFile : () => 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 Config = {
apiUrl: string;
timeout: number;
}
Config , 'FileNotFound'>;
declare const const loadConfigFromEnv: () => Result.ResultAsync<Config, "EnvNotSet"> loadConfigFromEnv : () => 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 Config = {
apiUrl: string;
timeout: number;
}
Config , 'EnvNotSet'>;
const const config: Result.Result<Config, "EnvNotSet"> config = await import Result Result .const pipe: <Result.ResultAsync<Config, "FileNotFound">, Result.ResultAsync<Config, "EnvNotSet">>(a: Result.ResultAsync<Config, "FileNotFound">, ab: (a: Result.ResultAsync<Config, "FileNotFound">) => Result.ResultAsync<Config, "EnvNotSet">) => Result.ResultAsync<Config, "EnvNotSet"> (+25 overloads) pipe (
const loadConfigFromFile: () => Result.ResultAsync<Config, "FileNotFound"> loadConfigFromFile (),
import Result Result .const orElse: <Result.ResultAsync<Config, "FileNotFound">, Result.ResultAsync<Config, "EnvNotSet">>(fn: (a: "FileNotFound") => Result.ResultAsync<Config, "EnvNotSet">) => (result: Result.ResultAsync<Config, "FileNotFound">) => Result.ResultAsync<Config, "EnvNotSet"> (+1 overload) orElse (() => const loadConfigFromEnv: () => Result.ResultAsync<Config, "EnvNotSet"> loadConfigFromEnv ()),
);
// まずファイルを試し、次に環境変数を試し、それでもダメなら失敗#andThen と orElse の組み合わせ
両方の関数を一緒に使って、成功時の連結とエラー回復の両方を含む複雑なフローを作成できます。
import { import Result Result } from '@praha/byethrow';
type type User = {
id: string;
name: string;
}
User = { id: string id : string; name: string name : string };
declare const const fetchUserFromCache: (id: string) => Result.ResultAsync<User, "CacheMiss"> fetchUserFromCache : (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.@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 User = {
id: string;
name: string;
}
User , 'CacheMiss'>;
declare const const fetchUserFromDb: (id: string) => Result.ResultAsync<User, "NotFound"> fetchUserFromDb : (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.@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 User = {
id: string;
name: string;
}
User , 'NotFound'>;
declare const const validateUser: (user: User) => Result.Result<User, "Invalid"> validateUser : (user: User user : type User = {
id: string;
name: string;
}
User ) => import Result Result .type Result<T, E> = Result.Success<T> | Result.Failure<E>A union type representing either a success or a failure.
@typeParamT - The type of the Success value.@typeParamE - The type of the Failure value.@exampleimport { Result } from '@praha/byethrow';
const doSomething = (): Result.Result<number, string> => {
return Math.random() > 0.5
? { type: 'Success', value: 10 }
: { type: 'Failure', error: 'Oops' };
};
@categoryCore Types Result <type User = {
id: string;
name: string;
}
User , 'Invalid'>;
const const getValidatedUser: (id: string) => Result.ResultAsync<User, "NotFound" | "Invalid"> getValidatedUser = (id: string id : string) => import Result Result .const pipe: <Result.ResultAsync<User, "CacheMiss">, Result.ResultAsync<User, "NotFound">, Result.ResultAsync<User, "NotFound" | "Invalid">>(a: Result.ResultAsync<User, "CacheMiss">, ab: (a: Result.ResultAsync<User, "CacheMiss">) => Result.ResultAsync<User, "NotFound">, bc: (b: Result.ResultAsync<User, "NotFound">) => Result.ResultAsync<User, "NotFound" | "Invalid">) => Result.ResultAsync<User, "NotFound" | "Invalid"> (+25 overloads) pipe (
const fetchUserFromCache: (id: string) => Result.ResultAsync<User, "CacheMiss"> fetchUserFromCache (id: string id ),
import Result Result .const orElse: <Result.ResultAsync<User, "CacheMiss">, Result.ResultAsync<User, "NotFound">>(fn: (a: "CacheMiss") => Result.ResultAsync<User, "NotFound">) => (result: Result.ResultAsync<User, "CacheMiss">) => Result.ResultAsync<User, "NotFound"> (+1 overload) orElse (() => const fetchUserFromDb: (id: string) => Result.ResultAsync<User, "NotFound"> fetchUserFromDb (id: string id )),
import Result Result .const andThen: <Result.ResultAsync<User, "NotFound">, Result.Result<User, "Invalid">>(fn: (a: User) => Result.Result<User, "Invalid">) => (result: Result.ResultAsync<User, "NotFound">) => Result.ResultAsync<User, "NotFound" | "Invalid"> (+1 overload) andThen (const validateUser: (user: User) => Result.Result<User, "Invalid"> validateUser ),
);
// まずキャッシュを試し、DB にフォールバックし、その後結果をバリデート#リファレンス
| 関数 | 目的 |
|---|---|
| andThen(fn) | 成功値を使って計算を連結する |
| orElse(fn) | 失敗から新しい計算で回復する |
