見出し画像

fp-tsライブラリを使ったTaskEither型でDo notationするときのワークアラウンドの紹介

こんにちわ。nap5です。

fp-tsライブラリを使ったTaskEither型でDo notationするときのワークアラウンドの紹介をしたいと思います。



bindで前回のステップ実行結果を変数に束縛しつつ、今回のステップ処理の引数に渡せているのがポイントです。束縛する変数名を自身で定義できる点もポイントです。


import { pipe } from "fp-ts/lib/function";
import * as TE from "fp-ts/lib/TaskEither";

type Category = "Cat" | "Dog";

type Pet = {
  id: number;
  name: string;
  category: Category;
};

type User = {
  id: number;
  name: string;
  pets: Pet[];
};

interface UserFactory {
  getUser(id: number): TE.TaskEither<Error, User>;
  hasDogUser(user: User): TE.TaskEither<Error, boolean>;
}

class UserRepository implements UserFactory {
  hasDogUser(user: User): TE.TaskEither<Error, boolean> {
    if (!user.pets.map((d) => d.category).includes("Dog")) {
      return TE.left(new Error("This user has no Dog."));
    }
    return TE.right(true);
  }
  getUser(id: number): TE.TaskEither<Error, User> {
    if (id % 2 === 0) {
      return TE.left(new Error("Not allow user."));
    }
    const mockUser: User = {
      id,
      name: "Cowboy",
      pets: [
        {
          id: 1,
          name: "Nut",
          category: "Dog",
        },
      ],
    };
    return TE.right(mockUser);
  }
}

const userRepository = new UserRepository();

(async () => {
  const result = await pipe(
    TE.Do,
    TE.bind("userId", () => TE.of(3)),
    // TE.bind("userId", () => TE.fromNullable(new Error("Mock"))(1)),
    TE.bind("user", ({ userId }) => userRepository.getUser(userId)),
    TE.bind("hasDog", ({ user }) => userRepository.hasDogUser(user)),
    TE.map(
      ({ userId, user, hasDog }) =>
        `${user.name}[${userId}] has ${user.pets.length} pets and a ${
          hasDog ? "dog" : "some"
        }`
    )
  )();

  console.log(result);
})();


実行結果です。正常系を通る場合、束縛した値でmapを通して出力値を加工できることが確認できました。

$ yarn do-1
{ _tag: 'Right', right: 'Cowboy[3] has 1 pets and a dog' }


デモコードです。




簡単ですが、以上です。


この記事が気に入ったらサポートをしてみませんか?