Manav Chawla
Manav Chawla

Reputation: 61

Is there a Do/Bind pattern to create an object containing different types in fp-ts?

I’m looking for something like Record.Do with Record.bind so I can do something like this

function getB: (a:A) => B
function getC: (b: B) => C
function getD: (c: C) => D

type Z = {
  a: A,
  b: B,
  c: C,
  d: D,
}

const getZ = (a: A): Z => pipe(
  R.Do,
  R.bind('a', () => a),
  R.bind('b', () => getB(a)),
  R.bind('c', (bindings) => getC(bindings.b)),
  R.bind('d', (bindings) => getD(bindings.c)),
)

I basically want to construct an object of different types while retaining all the inner objects of different types before applying some transformations on them

Not sure how to go about achieving this. I don’t want to take my types to other domains like Option, Either, IO; I feel like that just adds more code using O.some(s) or E.right(s) or IO.of(s) for transformations that don’t error.

This is the closes I could get

const getZ = (a: A): Z => pipe(
  IO.Do,
  IO.bind('a', () => () => a),
  IO.bind('b', () => () => getB(a)),
  IO.bind('c', (bindings) => () => getC(bindings.b)),
  IO.bind('d', (bindings) => () => getD(bindings.c)),
)()

Upvotes: 0

Views: 403

Answers (2)

OlaoluwaM
OlaoluwaM

Reputation: 81

Why not just use the do notation on the Identity module? It's a type without any effects, so it should do what you want: https://gcanti.github.io/fp-ts/modules/Identity.ts.html#do-notation

Upvotes: 1

Manav Chawla
Manav Chawla

Reputation: 61

The imperative way to do this would be

const getZ = (a:A): Z => {
   const b = getB(a)
   const c = getC(b)
   const d = getD(c)
   return ({a, b, c, d})
}

More fp-ts, but still kinda imperative?

const getZ = (a: A): Z => pipe(
  a,
  (a) => ({ b: getB(a), a}),
  (bindings) => ({ c: getC(bindings.b), ...bindings })
  (bindings) => ({ d: getD(bindings.c), ...bindings })
)

Still looking for suggestions

Upvotes: 0

Related Questions