Skip to content

Instantly share code, notes, and snippets.

@ukslim
Last active May 24, 2024 13:09
Show Gist options
  • Save ukslim/a30241a19949ad33bf59ffe04a19403b to your computer and use it in GitHub Desktop.
Save ukslim/a30241a19949ad33bf59ffe04a19403b to your computer and use it in GitHub Desktop.
Refactoring from Ramda

Refactor away from Ramda

I love Ramda, but my organisation is falling away from it - we have hires who don't think in FP, and Typescript and Ramda fight somewhat.

You can coax Ramda code to work in Typescript by strategically adding as in the right places. But it's often quite hard work -- harder work than refactoring away from Ramda before converting the plain JS to TS.

Here's some patterns for refactoring away from Ramda. In most cases, applying any of these changes leaves you with less clear code than you started with. But you can now apply more refactorings - extract function, extract method, pulling up IIAF parameters then removing unnecessary IIAF calls, will lead you towards clean code.

You can normally use a meaningless parameter name like x as in these examples, then continue refactoring until it's no longer there. Perhaps at the top level it will remain. Rename it to be meaningful.

I don't try to be exhaustive. Many conversions are obvious (indeed, these ones are!) and once you get the general idea, you'll find it easy to convert functions not described here.

It's often valuable to extract-function or extract-variable until functions calls are boiled down to their essence. Once you have refactored those, you can use IDE tooling to inline the refactored code.

compose

Before:

compose(f1, f2, f3);

After:

x => f1(f2(f3(x)));

pipe

Rename to compose and reverse the parameter order. Test. Now refactor from compose.

Before:

pipe(f1, f2, f3)

After

compose(f3, f2, f1);

ifElse

Before

ifElse(pred, thenFn, elseFn)

After

x => pred(x) ? thenFn(x) : elseFn(x)

Notes

You can very often now refactor pred(x) into a simple predicate, for example:

 ifElse(propEq('brown', 'hair), setLabel('brunette'), setLabel('blonde'));
 -->
 x => propEq('brown', 'hair)(x) ? setLabel('brunette')(x) : setLabel('blonde')(x)
 -->
 x => x.hair == 'brown' ? setLabel('brunette')(x) : setLabel('blonde')(x)

... and continue simplifying from there.

'Convert to if-else' from your IDE's refactoring menu may now be valuable.

when

Before

when(pred, f1)

After

x => pred(x) ? f1(x) : x

Notes

cf ifElse

assoc

Before

assoc(label, value)

After

x => ({ ...x, [label]: value })

evolve

Before

evolve({
   name1: f1,
   name2: f2,
   name3: f3, 
})

After

x => ({
  ...x,
  name1: f1(x),
  name2: f2(x),
  name3: f3(x),
})

Converge

Before

converge(f0, [ f1, f2, f3 ])

After

x => f0(f1(x), f2(x), f3(x))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment