zl程序教程

您现在的位置是:首页 >  其它

当前栏目

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

-- Function Programming Transformers Monad
2023-09-14 09:00:47 时间

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();