Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

R.set/R.over both lose the prototype of manipulated object #2775

Open
Heimdell opened this issue Jan 28, 2019 · 1 comment
Open

R.set/R.over both lose the prototype of manipulated object #2775

Heimdell opened this issue Jan 28, 2019 · 1 comment

Comments

@Heimdell
Copy link

Some preparation:

> let R = require('./node_modules/ramda')
> class X { constructor(a) { this.a = a } }
> x = new X(2)
X { a: 2 }

What I expect:

> R.set(R.lensPath(["a"]), 4, x)
X { a: 4 }

What I get:

> R.set(R.lensPath(["a"]), 4, x)
{ a: 4 }

P.S.:
I had to do the following to circumvent the issue:

let patchRambdaSetter = action => (lens, value, source) =>
    Object.assign(
        Object.create(source.__proto__),
        action(lens, value, source)
    )

let Rset  = patchRambdaSetter(R.set)
let Rover = patchRambdaSetter(R.over)

Can you do the prototype setup on the Ramda side, please?
I can't think of current behavior as of intended one. The semantics of set/over are "modify an object", and not "modify object and set prototype to {}".

Heimdell added a commit to Heimdell/ramda that referenced this issue Jan 28, 2019
Heimdell added a commit to Heimdell/ramda that referenced this issue Jan 28, 2019
Heimdell added a commit to Heimdell/ramda that referenced this issue Jan 28, 2019
@CrossEye
Copy link
Member

I'm afraid I'm 👎 on this.

We've discussed similar ideas before, and generally come to the conclusion that Ramda can't keep trying to support OOP ideas alongside its functional concerns.

One of the most important points about JS is that it's flexible. It's like the old Perl mantra: There's more than one way to do it. . There are far too many ways that people do OOP for us to keep track of. If we were to concern ourselves with the prototype chain in all its guises, we'd never get anything else done.

Here's an example, tweaked from its earlier incarnation in a similar discussion nearly three years ago:

class Foo {
  constructor(_val) {
    let changeCount = 0;
    let val = _val;
    Object.defineProperties(this, {
      x: {
        get: function() {return val;},
        set: function(newVal) {changeCount++; val = newVal;},
        enumerable: true,
        configurable: false
      },
      changes: {
        get: function() {return changeCount;},
        enumerable: true,
        configurable: false
      }
    });
    
  }
}

let foo = new Foo(42);
console.log(foo)  //~> {x: 42, changes: 0}

foo.x = 43
console.log(foo)  //~> {x: 43, changes: 1}

foo.x = 100
console.log(foo)  //~> {x: 100, changes: 2}

const bar = Rset(lensPath('x'), 50, foo)    // Using enhanced `set`
console.log(bar)  //~> {x: 50, changes: 2}  // Hmm, shouldn't `changes` be 0?

bar.x = 101
console.log(bar)  //~> {x: 101, changes: 2} // `changes` should definitely be updated.

The constructor function here maintains some extra information. But that would not transfer over with this enhanced set. The problem is that just assigning the prototype is not enough. To capture a constructed object, we need to run the constructor again. But we can't because we don't know what to pass to it.

And that's a good part of why Ramda has switched to punting on OOP integrations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants