calmm-js / kefir.combines

Special purpose applicative Kefir combinator
MIT License
1 stars 1 forks source link

Bug: K() breaks references when mixing vanilla objects with observables #2

Closed rikutiira closed 7 years ago

rikutiira commented 7 years ago

Example of unexpected behavior:

var o = {}

K(o, o_ => console.log(o === o_)) //true
K(o, 'test', o_ => console.log(o === o_)) //true
K(Kefir.constant(o), o_ => console.log(o === o_)).onValue(() => {}) //true
K(Kefir.constant(o), Kefir.constant('test'), o_ => console.log(o === o_)).onValue(() => {}) //true
K(o, Kefir.constant('test'), o_ => console.log(o === o_)).onValue(() => {}) //false
rikutiira commented 7 years ago

I'm guessing the cause is the object copying here?

function combine(template, values, state) {
  if (template instanceof Observable) {
    return values[++state.index]
  } else if (isArray(template)) {
    const n = template.length
    const next = Array(n)
    for (let i=0; i<n; ++i)
      next[i] = combine(template[i], values, state)
    return next
  } else if (isObject(template)) {
    const next = {}
    for (const k in template)
      next[k] = combine(template[k], values, state)
    return next
  } else {
    return template
  }
}

I think it'd make sense for objects with no observables in them to always be references or then to always be copies, right now it's confusing since it's working in two different ways depending on how K is used.

Personally I'd prefer for combine to check if the object has any observables or not so that you can do reference checks using the variable which K returns. And it probably has less overhead than always copying objects.

polytypic commented 7 years ago

Ah... That is good point! I think originally the idea was only to produce structurally equal object/array structure, but I agree that it would be better to keep object identities intact whenever possible. Like you say, doing so could also improve "systemic" performance as it could be used to avoid some unnecessary allocations and reduce GC pressure. (IIRC, there is some ad-hoc logic in karet that does stuff like that, BTW.)