[Function Programming] Function modelling -- 9. Monad Transformers

4767 단어
Path: Compose Functors -> Monad Transformers -> Free Monad
 
Let's first see how much it sucks when dealing with nested Monads (without natural transformation).
const { TaskT, Task, Either } = require("../types");
const _ = require("lodash");

const users = [
  { id: 1, name: "Brian" },
  { id: 2, name: "Marc" },
  { id: 3, name: "Odette" },
];
const following = [
  { user_id: 1, follow_id: 3 },
  { user_id: 1, follow_id: 2 },
  { user_id: 2, follow_id: 1 },
];

const find = (table, query) =>
  Task.of(Either.fromNullable(_.find(table, query)));

const app = () =>
  find(users, { id: 1 }) // Task(Either(User))
    .chain((eitherUser) =>
      eitherUser.fold(Task.rejected, (user) =>
        find(following, { follow_id: user.id })
      )
    )
    .chain((eitherUser) =>
      eitherUser.fold(Task.rejected, (foUser) =>
        find(users, { id: foUser.user_id })
      )
    )
    .fork(console.error, (eu) => eu.fold(console.error, console.log));

app(); // { id: 2, name: 'Marc' }

 
As you can see, the code is trying to find your follower's follower. 
Each time we need to .chain() + .fold()... 
Then .fork() + .fold()...
Which is confusing.
 
To solve the problem, we can include Monad transform:
const TaskEither = TaskT(Either);

Redefine 'find' function:
const find = (table, query) =>
  TaskEither.lift(Either.fromNullable(_.find(table, query)));

 
Difference between .of() vs .lift():
// TaskEither.of(Either) --> Task(Either(Either))
// TaskEither.lift(Either) --> Task(Either)

 
Then we can simpify our app:
const app = () =>
  find(users, { id: 1 }) // Task(Either(User))
    .chain((user) => find(following, { follow_id: user.id }))
    .chain((foUser) => find(users, { id: foUser.user_id }))
    .fork(console.error, (eu) => eu.fold(console.log, console.log));

app();

 -- 
 
Full ocde:
const { TaskT, Task, Either } = require("../types");
const _ = require("lodash");

const TaskEither = TaskT(Either);

const users = [
  { id: 1, name: "Brian" },
  { id: 2, name: "Marc" },
  { id: 3, name: "Odette" },
];
const following = [
  { user_id: 1, follow_id: 3 },
  { user_id: 1, follow_id: 2 },
  { user_id: 2, follow_id: 1 },
];

const find = (table, query) =>
  TaskEither.lift(Either.fromNullable(_.find(table, query)));

const app = () =>
  find(users, { id: 1 }) // Task(Either(User))
    .chain((user) => find(following, { follow_id: user.id }))
    .chain((foUser) => find(users, { id: foUser.user_id }))
    .fork(console.error, (eu) => eu.fold(console.log, console.log));

app();

좋은 웹페이지 즐겨찾기