• 日本語
  • 関数を合成する

    andThenorElse 関数を使うと、それ自体が Result を返す可能性のある計算を連結できます。 値を変換するだけの mapmapError とは異なり、これらの関数は各ステップが成功または失敗する可能性のある順次処理を可能にします。

    andThen - 成功時の計算を連結

    andThen 関数は成功値を使って次の計算を連結します。 元の ResultFailure の場合は、変更されずにそのまま返されます。 それ以外の場合、提供された関数が呼び出され、その結果がそのまま返されます。

    基本的な使い方

    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.@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;
        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.@example
    import { 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.@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;
        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 関数はエラー値を使って次の計算を連結します。 元の ResultSuccess の場合は、変更されずにそのまま返されます。 それ以外の場合、提供された関数が呼び出され、その結果がそのまま返されます。

    これはエラー回復に便利で、フォールバック値を提供したり、異なる戦略でリトライしたりできます。

    基本的な使い方

    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.@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 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.@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 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
    ()),
    ); // まずファイルを試し、次に環境変数を試し、それでもダメなら失敗

    andThenorElse の組み合わせ

    両方の関数を一緒に使って、成功時の連結とエラー回復の両方を含む複雑なフローを作成できます。

    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.@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
    , '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.@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
    , '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.@example
    import { 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)失敗から新しい計算で回復する