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' }
デモコードです。
簡単ですが、以上です。