1

I'm trying to figure out how to use fp-ts bind() function to automatically bind multiple properties from an object to the scope in one go:

import * as A from "fp-ts/Array";
import * as IO from "fp-ts/IO";

const wrap2 = (x: unknown) => () => () => x;
const wrap3 = (x: unknown) => () => () => () => x;
const bindIOEntry = (entry: [string, unknown]) =>
  IO.bind(entry[0], wrap3(entry[1]));

const person = {
  title: "Mr.",
  name: "John",
  surname: "Doe",
};

const result = pipe(
  IO.Do,
  ...A.map(bindIOEntry)(Object.entries(person)),
  IO.bind("tag", ({ title, name, surname }) =>
    wrap2(title() + " " + name() + " " + surname())
  )
);

console.log(result().tag()); // Mr. John Doe

The code above works perfectly fine, however, I do get an error on the array destructuring line:

A spread argument must either have a tuple type or be passed to a rest parameter.ts(2556)

I've tried it out with simpler pipes & it seems to be a general issue with using destructuring inside a pipe. Is there a solution or any kind of workaround for being able to do multiple binds in one go like this?

Adam B.
  • 788
  • 5
  • 14
  • You could do something like this https://tsplay.dev/wE76Zm not sure why `"tag"` is compared against `never` after that though – geoffrey Mar 01 '23 at 17:06
  • Why are you lifting precomputed values into the IO monad? If you just want to use bind on values you already have that are not wrapped in any context, use the Identity monad. – user1713450 Mar 21 '23 at 03:40

1 Answers1

0

Assuming we have

type Person = {
  title: string
  name: string
  surname: string
}

and you want to get it in the IO monad so you can use IO.bind, then just do this:

const liftAndTag = (a: Person) => pipe(
  IO.of(a),
  IO.bind('tag', ({ title, name, surname }) => IO.of(`${title} ${name} ${surname}`)
)

Although this is a hell of a lot of work when you don't need the IO monad at all. Given this exact initial data, if I wanted to use bind I would use the Identity monad. Then you don't even need that ugly IO.of(...) as the last instruction.

const l = (a: Person) => pipe(
  ID.of(a),
  ID.bind('tag', ({ name, title, surname }) => `${title} ${name} ${surname}`)
)

Edit This is another option if you prefer to use flow rather than pipe

const l2 = flow(
  ID.of,
  ID.bind('tag', ({ name, title, surname }: Person) => `${title} ${name} ${surname}`)
)
user1713450
  • 1,307
  • 7
  • 18