3

In the team we're trying to decide, if we should use ramda in our project, or not. The project is on Typesctipt, with React in the front and Nestjs in the back.

Currently the lists of pros and cons look like this

Pros:

  1. The code is easy to read
  2. The pure functions are easy to test and to refactor

Cons:

  1. Difficult to read, if you are new to ramda
  2. Ramda has problems with types, e.g.
interface Obj {p1: {p2: number}}

const obj: Obj = {
    p1: {
        p2: 1,
    }
}

const value = pathOr(null, ['p1', 'p2'], obj); //type of "value" will be "any"
  1. Using ramda with classes looks awkward, e.g.
class Cl {
  method1() {...}

  method2() {...}

  method3() {
    return compose(
      this.method1.bind(this),
      this.method2.bind(this),
    )()
  }
}

Will be glad to see any considerations on these points, or more pros and cons to add. Thanks.

Updated:

Taking into account geoffrey's comment, when it's impossible not to use classes (like in NestJS), it's probably better to split fp-style functions from class methods, because they are pure functions anyway:

const method1Fn = () => {...};
const method2Fn = () => {...};
const method3Fn = () => compose(
  method1Fn,
  method2Fn,
)

class Cl {
  method3() {
    return method3Fn()
  }
}

or even

class Cl {
  method3 = method3Fn
}

but in this case method3 will be created for every instance of Cl.

phaeton
  • 111
  • 1
  • 1
  • 6
  • 4
    I voted to close as this is opinion-based. But if you want my opinion (biased as one of the founders of Ramda), Ramda can work with Typescript, but it's not Ramda's strength, and there will be some small friction. But Ramda is very much not designed to work with classes; if they're a large part of you app, you probably want to skip Ramda or at least not intermingle Ramda and classes. I think readability is probably net-positive, so long as you don't overdo point-free (see Ori Drori's answer); once accustomed to it, it can be *much* more readable. And pure testable functions are a huge win. – Scott Sauyet Apr 23 '21 at 12:52
  • 1
    I too find #3 awkward because you both have point-free code and free variables, which is too much magic. If you compose functions with a tacit style, I believe they should take arguments and they should not be methods. Something to consider is that if you can truly compose multiple "pure methods" as in your example, it could mean that they are a different concern and should be extracted from the class. I also find that FP patterns work better with OO when you create stateless objects which are manipulated by functions, but when you do the opposite, classes get in the way. – geoffrey Apr 23 '21 at 21:53

2 Answers2

2

Readability in this case is a matter of usage. As long as you keep Ramda's usage to the places were it's easy to use an readable, it won't be a problem.

Examples from this answer

This is very readable:

const siblings = me => reject(equals(me), family(me));

While this overuses Ramda, and is less readable (and would probably require manual types):

const siblings = converge(reject, [unary(equals), family])

Ramda has problems with types, e.g.

Although you can install @types/ramda the inference is partial. You'll probably find yourself adding types manually:

interface Obj {
  p1: { p2: number };
}

const obj: Obj = {
  p1: {
    p2: 1
  }
};

const fn: (obj: Obj) => null | number = pathOr(null, ['p1', 'p2']);

const value = fn(obj);

console.log(value);

Using ramda with classes looks awkward, e.g.

You can use class properties with arrow functions to bind methods, so this is not actually an issue:

class Cl {
  method1 = () => {...}

  method2 = () => {...}

  method3() {
    return compose(
      this.method1,
      this.method2,
    )()
  }
}
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
2

When the codebase grows there will be lot of Ramda and when new developer comes to the project he will be terrified and will need to spend additional time to learn Ramda, which is not ok.

T1000
  • 2,909
  • 7
  • 36
  • 53