Object.observe feedback
BTW, here's your example using observe-js:
BTW, here's your example using observe-js: http://jsbin.com/leh/1/edit On Sun, Feb 16, 2014 at 7:50 AM, Rafael Weinstein <rafaelw at google.com>wrote: > Hi Don, > > Thanks for the thoughtful feedback. I'm very glad you're excited about the > feature. The short answer to your question is: the data you want is > provided, but your processing needs to be more sophisticated. > > [BTW, The Chrome/V8 implementation (to the best of my knowledge) fully > implements the latest spec for Object.observe] > > Basically, you're making an assumption about processing log data which is > understandable, but unfortunately false. Though it would be nice, it's > simply not possible to process each change record by looking at the final > state of the observed object. > > Object.observe provides full information about the changes that occur, but > depending on what view you want, you may need to do some processing to get > the right answer. > > Based on the use-case you describe, it sounds like you have a very common > desire -- to synchronize two objects, one of which has changed. The pattern > requires what I'll call a "diff in time" view of the changes. In other > words: from time 0 to time 1, what's the minimal set of changes I would > need to apply to a copy of the observed object at time 0 in order to > transform it into a copy of object at time 1? > > As you've found, Object.observe doesn't provide you this view of things. > It provides you with a log of what happened along the way. However, you can > use the log view as input into a transform which will provide you with the > diff view. > > I've provided a JS library which serves multiple purposes: > > -A handy library for those who want the "diff" view of things > -A reference implementation of some of these algorithms > -A polyfill so that you may use this functionality in browsers that don't > implement Object.observe (the polyfil mode has less desirable performance > -- specifically the cost to determine what has changed is proportional to > the total set of observed objects -- as opposed to Object.observe where the > cost is proportional to the number of changes which took place). > > The library is here: https://github.com/Polymer/observe-js > > It may be sufficient for your use case (as well as act as the polyfill you > were attempting to create) or it may simply serve as documentation for the > processing that is required. > > Thanks again for your feedback and have fun. > > Cheers > Rafael > > > > On Sun, Feb 16, 2014 at 5:25 AM, Woodlock, Don (GE Healthcare) < > Don.Woodlock at med.ge.com> wrote: > >> HI Rafael et al, >> >> I have some feedback into the Object.observe proposal. >> Now I may be missing something so any clarification or education would be >> appreciated as well. But I do love this particular spec and can’t wait to >> see it implemented. But while I was implementing a pseudo-polyfill of it >> for my own purposes, I noticed an ‘information hole’ in the ChangeRecords >> design. This is due to the asynchronous nature of the notification >> delivery and that subsequent mutations may have occurred by the time the >> notification is ‘delivered’. In particular I believe a new ‘added’ >> property is needed to the ChangeRecords of the splice transaction of an >> array mutation in the Array.observe design. >> >> Here’s the particular situation: >> >> First of all I’m assuming that the purpose of the >> ChangeRecords is to give the ChangeObserver enough information, beyond the >> fact that a change occurred, so that it doesn’t have to reprocess >> everything, but can be specific and efficient based on what in particular >> has changed. For example if I have a view that lists a bunch of students >> and it’s observing an array of students, if a student gets added to the >> array, I’d like to know enough so the view can just add the one student and >> doesn’t have to redisplay the entire list because it knows the array has >> changed somehow. Secondly I’ll assuming that the Chrome Canary >> implementation of Array.observe is suitably similar to the spec as that’s >> where I have been learning about what this design looks like in practice. >> >> Let’s say you have the array: [“c”, “d”, “f”]. If you >> are observing the array, and this operation occurs myArray.unshift(“b”), >> you will get the following notification: >> >> type = “splice”, >> >> removed = [], >> >> object: pointer to the array >> >> index: 0 >> >> addedCount: 1 >> >> >> >> You would naturally determine what was >> added via changeRecord.object.slice(changeRecord.index, changeRecord.index >> + changeRecord.addedCount); That would show you that “b” got added. >> >> Subsequently (and with a delay), if you did >> myArray.unshift(“a”);, you will get an identically looking notification, >> and through the same approach, you would see that “a” got added. You can >> see this if you run the first attachment in Chrome Canary. If you run >> this, you can see in the console log that the fact that the changeObserver >> determines ‘b’ and then ‘a’ were added appropriately. >> >> >> >> The problem exists if I don’t put a delay between the two >> unshift operations. If you look at the second attachment, I remove the >> delay so that myArray.unshift(“b”) and then myArray.unshift(“a”) happen one >> after another. The first notification goes out to the ChangeObserver >> asynchronously and after the second mutation occurs. So when processing >> that first notification, using the approach above, I use the index and >> addedCount against the current version of the array (after both mutations) >> and I determine that “a” was added when it was really “b” because “a” is >> now in index 0. After I process the second notification, I also think that >> “a” got added. You’ll see this if you run the second attachment in >> Canary. So I completely miss that “b” was ever added and could also >> reasonably assume that “a” was added again. >> >> So that’s the problem, as an observer you would like >> enough information in the ChangeRecords to process only what has changed, >> but you would essentially miss that “b” was added to the array because that >> information is not supplied. In this design, you need to infer what was >> added (vs. what was removed which is specified), by looking at the current >> object, but again subsequent mutations could have occurred so some changes >> are obscured in this design and you would be forced to reprocess everything >> – which is not the point of ChangeRecords. >> >> So my recommendation is that a data property named >> “added” to added to the ChangeRecord of a splice transaction to show the >> array elements that were added during the mutation that led to that >> particular ChangeRecord. You may have been trying in this design to not >> pass information that is current and rather have the user reference the >> array directly. But the ‘added’ property is not necessary repeating >> current information because of the subsequent mutations. It would be a >> useful glimpse of the past similar to the removed property. >> >> >> >> Object.observe has a similar problem but less problematic >> in practice. If you change a property on an object being observed to a >> different value and then change it back, it would not be possible for the >> observer of the first notification to see what the property was changed >> to. A ‘newValue’ property, for the same reasons as above, would be a >> useful addition even though in many cases, not all, it is the same as could >> be ascertained by looking at the current object directly. >> >> >> >> Again I may be confused so any education would be >> appreciated. But it appears that this is a big information hole that would >> cause ChangeObservers to miss when elements get added to arrays which >> defeats the point of these ChangeRecords. I hope this was clear and >> useful. Any questions or clarifications on this explanation, let me know. >> Thx. >> >> Don >> >> >> >> >> >> *Don Woodlock* >> >> GM, Cardiology IT >> >> GE Healthcare IT >> >> >> >> T +1 847 277 5515 >> >> C +1 781 608 7743 >> >> don.woodlock at ge.com >> >> www.gehealthcare.com >> >> >> >> 540 W. Northwest Hwy >> >> Barrington IL 60010 >> >> >> >> GE Imagination at work >> >> >> > > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20140216/72a13285/attachment.html>
On Thu, Feb 20, 2014 at 4:38 PM, Woodlock, Don (GE Healthcare) <Don.Woodlock at med.ge.com> wrote:
As you recommended, I did have some fun with this. And I now have some updated feedback:
I now understand that there is not indeed an ‘information hole’ in the changeRecords for the Array.observe callback. As you said you just need a more sophisticated processing algorithm to read them properly. I wrote a few different versions of this as well as stepped through your projectArraySplices () code in observe.js/Polymer. This logic is not for the faint of heart.
My feedback therefore is about the same. I believe that you should have an easier to use record of changes that is sent to the callback so that a novice or intermediate Javascript engineer can easily understand and use it. In fact you may be agreeing with this point. At the same time you are championing this standard, you are also writing a library (e.g. ArrayObserver in Polymer) that provides a more user-friendly face for the callback (your splices array). Why save the user-friendliness for the library, why not take the opportunity right now to make the changeRecords easier to use natively?
Here is the perspective that I am taking. At the moment I’m teaching my teenagers Javascript for an app they are building and in the interest of not confusing them, I’m trying to stick to VanillaJS so they don’t get confused about what is JS and what is part of a library. And I’m also interested in teaching them the MVC pattern which is difficult to do in pure JS. With Array.observe and Object.observe, I was hoping that eventually they, and users like them, could also take advantage of these new native features to write their code in MVC form.
But it is quite difficult to read the changeRecords as I mentioned above. So I took the perspective of an author writing an explanation of how to process them. You can see that in the attached document. This includes five different potential approaches that one can use to do a very simple MVC. Please read through it and you can see how difficult it is to explain it – at least for me.
So here would be my questions/feedback for you:
Why not offer a more user-friendly array of changeRecords that an intermediate engineer can properly process. For example your splices(or my supplemented changeRecords) are much much easier to deal with. Again why save that for a library, when we could just make ECMA 6 easy to use. You might say your splices can’t be used as a replacement because it leaves off mutations that don’t net to anything. But it does work for the three use cases in your youtube walk through – MVC, persistence, and constraint management. If Object.observe is just a performance gift for library authors, fine. But if it’s something for the masses, I think you could make it easier. I realize I’m disregarding other considerations – consistency with other parts of Javascript, performance considerations, etc. But you are soooo close to offering an easy to use native feature here.
Given the five solutions in my document, as the champion of Object.observe, what solution would you recommend someone use to process the records? Use a library? Keep a copy and just compare yourself? Your insight into how you expect this spec to be used would be appreciated.
Thanks Rafael – again I love the standard. I may still be misunderstanding how this works. But you asked for feedback. Let me know your thoughts.
P.S. – happy to add any of this to jsbin if you’d like to see it more closely with my testing scripts.
You don't misunderstand at all! You've totally got it now. It's awesome that you are using modern application architecture as teaching material.
As to your question about why not make the API easier to use, my answer is: one person's easy-to-use is another person's performance problem.
As language designers, it's our responsibility to add features to the language that are simple, flexible & powerful primitives which can enable diverse patterns of use that are highly performant. While adding an extra array to each change record may make it possible to process a set of use cases by simply "applying" each change record in order to a target, allocating those arrays has a cost that most "production" applications wish to avoid.
Also, most of the time it's a good trade-off to do a bunch of reads to avoid doing some writes (figuring out the minimal set of changes needed to synchronize). Also, it's generally a good idea to do some lost-cost work (computing the 'net-effect of what happened) to avoid some high-cost work (mutating the DOM).
As to my recommendation of which approach to use, my personal opinion is that good system design is frequently about proper separation of concerns and layering. Much like MVC patterns create conceptual leverage by separating "business logic" from "presentation logic" concerns, complex systems are possible when we create progressively complex layers of abstractions.
In other words, I think your use case requires layering the "diff in time" abstraction on top of the "raw stream of things that happened" abstraction. Whether you create this abstraction yourself or you use someone else's code (a js library) is up to you. I happen to be a fan of not reinventing the wheel when possible, but sometimes the most powerful teaching tool is to ask your student to do just that.
Don, You don't misunderstand at all! You've totally got it now. It's awesome that you are using modern application architecture as teaching material. As to your question about why not make the API easier to use, my answer is: one person's easy-to-use is another person's performance problem. As language designers, it's our responsibility to add features to the language that are simple, flexible & powerful primitives which can enable diverse patterns of use that are highly performant. While adding an extra array to each change record may make it possible to process a set of use cases by simply "applying" each change record in order to a target, allocating those arrays has a cost that most "production" applications wish to avoid. Also, most of the time it's a good trade-off to do a bunch of reads to avoid doing some writes (figuring out the minimal set of changes needed to synchronize). Also, it's generally a good idea to do some lost-cost work (computing the 'net-effect of what happened) to avoid some high-cost work (mutating the DOM). As to my recommendation of which approach to use, my personal opinion is that good system design is frequently about proper separation of concerns and layering. Much like MVC patterns create conceptual leverage by separating "business logic" from "presentation logic" concerns, complex systems are possible when we create progressively complex layers of abstractions. In other words, I think your use case requires layering the "diff in time" abstraction on top of the "raw stream of things that happened" abstraction. Whether you create this abstraction yourself or you use someone else's code (a js library) is up to you. I happen to be a fan of not reinventing the wheel when possible, but sometimes the most powerful teaching tool is to ask your student to do just that. Cheers R On Thu, Feb 20, 2014 at 4:38 PM, Woodlock, Don (GE Healthcare) < Don.Woodlock at med.ge.com> wrote: > Hi Rafael, > > As you recommended, I did have some fun with this. And I > now have some updated feedback: > > - I now understand that there is not indeed an ‘information > hole’ in the changeRecords for the Array.observe callback. As you said you > just need a more sophisticated processing algorithm to read them properly. > I wrote a few different versions of this as well as stepped through your > projectArraySplices () code in observe.js/Polymer. This logic is not for > the faint of heart. > > - My feedback therefore is about the same. I believe that you > should have an easier to use record of changes that is sent to the callback > so that a novice or intermediate Javascript engineer can easily understand > and use it. In fact you may be agreeing with this point. At the same time > you are championing this standard, you are also writing a library (e.g. > ArrayObserver in Polymer) that provides a more user-friendly face for the > callback (your splices array). Why save the user-friendliness for the > library, why not take the opportunity right now to make the changeRecords > easier to use natively? > > - Here is the perspective that I am taking. At the moment I’m > teaching my teenagers Javascript for an app they are building and in the > interest of not confusing them, I’m trying to stick to VanillaJS so they > don’t get confused about what is JS and what is part of a library. And I’m > also interested in teaching them the MVC pattern which is difficult to do > in pure JS. With Array.observe and Object.observe, I was hoping that > eventually they, and users like them, could also take advantage of these > new native features to write their code in MVC form. > > - But it is quite difficult to read the changeRecords as I > mentioned above. So I took the perspective of an author writing an > explanation of how to process them. You can see that in the attached > document. This includes five different potential approaches that one can > use to do a very simple MVC. Please read through it and you can see how > difficult it is to explain it – at least for me. > > > > So here would be my questions/feedback for you: > > - Why not offer a more user-friendly array of changeRecords that > an intermediate engineer can properly process. For example your *splices*(or my supplemented changeRecords) are much much easier to deal with. > Again why save that for a library, when we could just make ECMA 6 easy to > use. You might say your splices can’t be used as a replacement because it > leaves off mutations that don’t net to anything. But it does work for the > three use cases in your youtube walk through – MVC, persistence, and > constraint management. If Object.observe is just a performance gift for > library authors, fine. But if it’s something for the masses, I think you > could make it easier. I realize I’m disregarding other considerations – > consistency with other parts of Javascript, performance considerations, > etc. But you are soooo close to offering an easy to use native feature > here. > > - Given the five solutions in my document, as the champion of > Object.observe, what solution would you recommend someone use to process > the records? Use a library? Keep a copy and just compare yourself? Your > insight into how you expect this spec to be used would be appreciated. > > > > Thanks Rafael – again I love the standard. I may still be > misunderstanding how this works. But you asked for feedback. Let me know > your thoughts. > > > > Don > > P.S. – happy to add any of this to jsbin if you’d like to see it more > closely with my testing scripts. > > > > > > > > > > > > *From:* Rafael Weinstein [mailto:rafaelw at google.com] > *Sent:* Sunday, February 16, 2014 8:06 AM > *To:* Woodlock, Don (GE Healthcare) > *Cc:* es-discuss at mozilla.org; dfwoodlock at gmail.com > *Subject:* Re: Object.observe feedback > > > > BTW, here's your example using observe-js: > > > > http://jsbin.com/leh/1/edit > > > > On Sun, Feb 16, 2014 at 7:50 AM, Rafael Weinstein <rafaelw at google.com> > wrote: > > Hi Don, > > > > Thanks for the thoughtful feedback. I'm very glad you're excited about the > feature. The short answer to your question is: the data you want is > provided, but your processing needs to be more sophisticated. > > > > [BTW, The Chrome/V8 implementation (to the best of my knowledge) fully > implements the latest spec for Object.observe] > > > > Basically, you're making an assumption about processing log data which is > understandable, but unfortunately false. Though it would be nice, it's > simply not possible to process each change record by looking at the final > state of the observed object. > > > > Object.observe provides full information about the changes that occur, but > depending on what view you want, you may need to do some processing to get > the right answer. > > > > Based on the use-case you describe, it sounds like you have a very common > desire -- to synchronize two objects, one of which has changed. The pattern > requires what I'll call a "diff in time" view of the changes. In other > words: from time 0 to time 1, what's the minimal set of changes I would > need to apply to a copy of the observed object at time 0 in order to > transform it into a copy of object at time 1? > > > > As you've found, Object.observe doesn't provide you this view of things. > It provides you with a log of what happened along the way. However, you can > use the log view as input into a transform which will provide you with the > diff view. > > > > I've provided a JS library which serves multiple purposes: > > > > -A handy library for those who want the "diff" view of things > > -A reference implementation of some of these algorithms > > -A polyfill so that you may use this functionality in browsers that don't > implement Object.observe (the polyfil mode has less desirable performance > -- specifically the cost to determine what has changed is proportional to > the total set of observed objects -- as opposed to Object.observe where the > cost is proportional to the number of changes which took place). > > > > The library is here: https://github.com/Polymer/observe-js > > > > It may be sufficient for your use case (as well as act as the polyfill you > were attempting to create) or it may simply serve as documentation for the > processing that is required. > > > > Thanks again for your feedback and have fun. > > > > Cheers > > Rafael > > > > > > On Sun, Feb 16, 2014 at 5:25 AM, Woodlock, Don (GE Healthcare) < > Don.Woodlock at med.ge.com> wrote: > > HI Rafael et al, > > I have some feedback into the Object.observe proposal. > Now I may be missing something so any clarification or education would be > appreciated as well. But I do love this particular spec and can’t wait to > see it implemented. But while I was implementing a pseudo-polyfill of it > for my own purposes, I noticed an ‘information hole’ in the ChangeRecords > design. This is due to the asynchronous nature of the notification > delivery and that subsequent mutations may have occurred by the time the > notification is ‘delivered’. In particular I believe a new ‘added’ > property is needed to the ChangeRecords of the splice transaction of an > array mutation in the Array.observe design. > > Here’s the particular situation: > > First of all I’m assuming that the purpose of the > ChangeRecords is to give the ChangeObserver enough information, beyond the > fact that a change occurred, so that it doesn’t have to reprocess > everything, but can be specific and efficient based on what in particular > has changed. For example if I have a view that lists a bunch of students > and it’s observing an array of students, if a student gets added to the > array, I’d like to know enough so the view can just add the one student and > doesn’t have to redisplay the entire list because it knows the array has > changed somehow. Secondly I’ll assuming that the Chrome Canary > implementation of Array.observe is suitably similar to the spec as that’s > where I have been learning about what this design looks like in practice. > > Let’s say you have the array: [“c”, “d”, “f”]. If you are > observing the array, and this operation occurs myArray.unshift(“b”), you > will get the following notification: > > type = “splice”, > > removed = [], > > object: pointer to the array > > index: 0 > > addedCount: 1 > > > > You would naturally determine what was > added via changeRecord.object.slice(changeRecord.index, changeRecord.index > + changeRecord.addedCount); That would show you that “b” got added. > > Subsequently (and with a delay), if you did > myArray.unshift(“a”);, you will get an identically looking notification, > and through the same approach, you would see that “a” got added. You can > see this if you run the first attachment in Chrome Canary. If you run > this, you can see in the console log that the fact that the changeObserver > determines ‘b’ and then ‘a’ were added appropriately. > > > > The problem exists if I don’t put a delay between the two > unshift operations. If you look at the second attachment, I remove the > delay so that myArray.unshift(“b”) and then myArray.unshift(“a”) happen one > after another. The first notification goes out to the ChangeObserver > asynchronously and after the second mutation occurs. So when processing > that first notification, using the approach above, I use the index and > addedCount against the current version of the array (after both mutations) > and I determine that “a” was added when it was really “b” because “a” is > now in index 0. After I process the second notification, I also think that > “a” got added. You’ll see this if you run the second attachment in > Canary. So I completely miss that “b” was ever added and could also > reasonably assume that “a” was added again. > > So that’s the problem, as an observer you would like > enough information in the ChangeRecords to process only what has changed, > but you would essentially miss that “b” was added to the array because that > information is not supplied. In this design, you need to infer what was > added (vs. what was removed which is specified), by looking at the current > object, but again subsequent mutations could have occurred so some changes > are obscured in this design and you would be forced to reprocess everything > – which is not the point of ChangeRecords. > > So my recommendation is that a data property named “added” > to added to the ChangeRecord of a splice transaction to show the array > elements that were added during the mutation that led to that particular > ChangeRecord. You may have been trying in this design to not pass > information that is current and rather have the user reference the array > directly. But the ‘added’ property is not necessary repeating current > information because of the subsequent mutations. It would be a useful > glimpse of the past similar to the removed property. > > > > Object.observe has a similar problem but less problematic > in practice. If you change a property on an object being observed to a > different value and then change it back, it would not be possible for the > observer of the first notification to see what the property was changed > to. A ‘newValue’ property, for the same reasons as above, would be a > useful addition even though in many cases, not all, it is the same as could > be ascertained by looking at the current object directly. > > > > Again I may be confused so any education would be > appreciated. But it appears that this is a big information hole that would > cause ChangeObservers to miss when elements get added to arrays which > defeats the point of these ChangeRecords. I hope this was clear and > useful. Any questions or clarifications on this explanation, let me know. > Thx. > > Don > > > > > > *Don Woodlock* > > GM, Cardiology IT > > GE Healthcare IT > > > > T +1 847 277 5515 > > C +1 781 608 7743 > > don.woodlock at ge.com > > www.gehealthcare.com > > > > 540 W. Northwest Hwy > > Barrington IL 60010 > > > > GE Imagination at work > > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20140221/1720c4ae/attachment-0001.html>
Fair enough. I certainly understand the very top priority of the native platform to be fast. Without that the layers built on top of it don’t stand a chance. Thanks for listening and explaining.
Fair enough. I certainly understand the very top priority of the native platform to be fast. Without that the layers built on top of it don’t stand a chance. Thanks for listening and explaining. From: Rafael Weinstein [mailto:rafaelw at google.com] Sent: Friday, February 21, 2014 8:25 AM To: Woodlock, Don (GE Healthcare) Cc: es-discuss at mozilla.org; dfwoodlock at gmail.com Subject: Re: Object.observe feedback Don, You don't misunderstand at all! You've totally got it now. It's awesome that you are using modern application architecture as teaching material. As to your question about why not make the API easier to use, my answer is: one person's easy-to-use is another person's performance problem. As language designers, it's our responsibility to add features to the language that are simple, flexible & powerful primitives which can enable diverse patterns of use that are highly performant. While adding an extra array to each change record may make it possible to process a set of use cases by simply "applying" each change record in order to a target, allocating those arrays has a cost that most "production" applications wish to avoid. Also, most of the time it's a good trade-off to do a bunch of reads to avoid doing some writes (figuring out the minimal set of changes needed to synchronize). Also, it's generally a good idea to do some lost-cost work (computing the 'net-effect of what happened) to avoid some high-cost work (mutating the DOM). As to my recommendation of which approach to use, my personal opinion is that good system design is frequently about proper separation of concerns and layering. Much like MVC patterns create conceptual leverage by separating "business logic" from "presentation logic" concerns, complex systems are possible when we create progressively complex layers of abstractions. In other words, I think your use case requires layering the "diff in time" abstraction on top of the "raw stream of things that happened" abstraction. Whether you create this abstraction yourself or you use someone else's code (a js library) is up to you. I happen to be a fan of not reinventing the wheel when possible, but sometimes the most powerful teaching tool is to ask your student to do just that. Cheers R On Thu, Feb 20, 2014 at 4:38 PM, Woodlock, Don (GE Healthcare) <Don.Woodlock at med.ge.com<mailto:Don.Woodlock at med.ge.com>> wrote: Hi Rafael, As you recommended, I did have some fun with this. And I now have some updated feedback: - I now understand that there is not indeed an ‘information hole’ in the changeRecords for the Array.observe callback. As you said you just need a more sophisticated processing algorithm to read them properly. I wrote a few different versions of this as well as stepped through your projectArraySplices () code in observe.js/Polymer. This logic is not for the faint of heart. - My feedback therefore is about the same. I believe that you should have an easier to use record of changes that is sent to the callback so that a novice or intermediate Javascript engineer can easily understand and use it. In fact you may be agreeing with this point. At the same time you are championing this standard, you are also writing a library (e.g. ArrayObserver in Polymer) that provides a more user-friendly face for the callback (your splices array). Why save the user-friendliness for the library, why not take the opportunity right now to make the changeRecords easier to use natively? - Here is the perspective that I am taking. At the moment I’m teaching my teenagers Javascript for an app they are building and in the interest of not confusing them, I’m trying to stick to VanillaJS so they don’t get confused about what is JS and what is part of a library. And I’m also interested in teaching them the MVC pattern which is difficult to do in pure JS. With Array.observe and Object.observe, I was hoping that eventually they, and users like them, could also take advantage of these new native features to write their code in MVC form. - But it is quite difficult to read the changeRecords as I mentioned above. So I took the perspective of an author writing an explanation of how to process them. You can see that in the attached document. This includes five different potential approaches that one can use to do a very simple MVC. Please read through it and you can see how difficult it is to explain it – at least for me. So here would be my questions/feedback for you: - Why not offer a more user-friendly array of changeRecords that an intermediate engineer can properly process. For example your splices (or my supplemented changeRecords) are much much easier to deal with. Again why save that for a library, when we could just make ECMA 6 easy to use. You might say your splices can’t be used as a replacement because it leaves off mutations that don’t net to anything. But it does work for the three use cases in your youtube walk through – MVC, persistence, and constraint management. If Object.observe is just a performance gift for library authors, fine. But if it’s something for the masses, I think you could make it easier. I realize I’m disregarding other considerations – consistency with other parts of Javascript, performance considerations, etc. But you are soooo close to offering an easy to use native feature here. - Given the five solutions in my document, as the champion of Object.observe, what solution would you recommend someone use to process the records? Use a library? Keep a copy and just compare yourself? Your insight into how you expect this spec to be used would be appreciated. Thanks Rafael – again I love the standard. I may still be misunderstanding how this works. But you asked for feedback. Let me know your thoughts. Don P.S. – happy to add any of this to jsbin if you’d like to see it more closely with my testing scripts. From: Rafael Weinstein [mailto:rafaelw at google.com<mailto:rafaelw at google.com>] Sent: Sunday, February 16, 2014 8:06 AM To: Woodlock, Don (GE Healthcare) Cc: es-discuss at mozilla.org<mailto:es-discuss at mozilla.org>; dfwoodlock at gmail.com<mailto:dfwoodlock at gmail.com> Subject: Re: Object.observe feedback BTW, here's your example using observe-js: http://jsbin.com/leh/1/edit On Sun, Feb 16, 2014 at 7:50 AM, Rafael Weinstein <rafaelw at google.com<mailto:rafaelw at google.com>> wrote: Hi Don, Thanks for the thoughtful feedback. I'm very glad you're excited about the feature. The short answer to your question is: the data you want is provided, but your processing needs to be more sophisticated. [BTW, The Chrome/V8 implementation (to the best of my knowledge) fully implements the latest spec for Object.observe] Basically, you're making an assumption about processing log data which is understandable, but unfortunately false. Though it would be nice, it's simply not possible to process each change record by looking at the final state of the observed object. Object.observe provides full information about the changes that occur, but depending on what view you want, you may need to do some processing to get the right answer. Based on the use-case you describe, it sounds like you have a very common desire -- to synchronize two objects, one of which has changed. The pattern requires what I'll call a "diff in time" view of the changes. In other words: from time 0 to time 1, what's the minimal set of changes I would need to apply to a copy of the observed object at time 0 in order to transform it into a copy of object at time 1? As you've found, Object.observe doesn't provide you this view of things. It provides you with a log of what happened along the way. However, you can use the log view as input into a transform which will provide you with the diff view. I've provided a JS library which serves multiple purposes: -A handy library for those who want the "diff" view of things -A reference implementation of some of these algorithms -A polyfill so that you may use this functionality in browsers that don't implement Object.observe (the polyfil mode has less desirable performance -- specifically the cost to determine what has changed is proportional to the total set of observed objects -- as opposed to Object.observe where the cost is proportional to the number of changes which took place). The library is here: https://github.com/Polymer/observe-js It may be sufficient for your use case (as well as act as the polyfill you were attempting to create) or it may simply serve as documentation for the processing that is required. Thanks again for your feedback and have fun. Cheers Rafael On Sun, Feb 16, 2014 at 5:25 AM, Woodlock, Don (GE Healthcare) <Don.Woodlock at med.ge.com<mailto:Don.Woodlock at med.ge.com>> wrote: HI Rafael et al, I have some feedback into the Object.observe proposal. Now I may be missing something so any clarification or education would be appreciated as well. But I do love this particular spec and can’t wait to see it implemented. But while I was implementing a pseudo-polyfill of it for my own purposes, I noticed an ‘information hole’ in the ChangeRecords design. This is due to the asynchronous nature of the notification delivery and that subsequent mutations may have occurred by the time the notification is ‘delivered’. In particular I believe a new ‘added’ property is needed to the ChangeRecords of the splice transaction of an array mutation in the Array.observe design. Here’s the particular situation: First of all I’m assuming that the purpose of the ChangeRecords is to give the ChangeObserver enough information, beyond the fact that a change occurred, so that it doesn’t have to reprocess everything, but can be specific and efficient based on what in particular has changed. For example if I have a view that lists a bunch of students and it’s observing an array of students, if a student gets added to the array, I’d like to know enough so the view can just add the one student and doesn’t have to redisplay the entire list because it knows the array has changed somehow. Secondly I’ll assuming that the Chrome Canary implementation of Array.observe is suitably similar to the spec as that’s where I have been learning about what this design looks like in practice. Let’s say you have the array: [“c”, “d”, “f”]. If you are observing the array, and this operation occurs myArray.unshift(“b”), you will get the following notification: type = “splice”, removed = [], object: pointer to the array index: 0 addedCount: 1 You would naturally determine what was added via changeRecord.object.slice(changeRecord.index, changeRecord.index + changeRecord.addedCount); That would show you that “b” got added. Subsequently (and with a delay), if you did myArray.unshift(“a”);, you will get an identically looking notification, and through the same approach, you would see that “a” got added. You can see this if you run the first attachment in Chrome Canary. If you run this, you can see in the console log that the fact that the changeObserver determines ‘b’ and then ‘a’ were added appropriately. The problem exists if I don’t put a delay between the two unshift operations. If you look at the second attachment, I remove the delay so that myArray.unshift(“b”) and then myArray.unshift(“a”) happen one after another. The first notification goes out to the ChangeObserver asynchronously and after the second mutation occurs. So when processing that first notification, using the approach above, I use the index and addedCount against the current version of the array (after both mutations) and I determine that “a” was added when it was really “b” because “a” is now in index 0. After I process the second notification, I also think that “a” got added. You’ll see this if you run the second attachment in Canary. So I completely miss that “b” was ever added and could also reasonably assume that “a” was added again. So that’s the problem, as an observer you would like enough information in the ChangeRecords to process only what has changed, but you would essentially miss that “b” was added to the array because that information is not supplied. In this design, you need to infer what was added (vs. what was removed which is specified), by looking at the current object, but again subsequent mutations could have occurred so some changes are obscured in this design and you would be forced to reprocess everything – which is not the point of ChangeRecords. So my recommendation is that a data property named “added” to added to the ChangeRecord of a splice transaction to show the array elements that were added during the mutation that led to that particular ChangeRecord. You may have been trying in this design to not pass information that is current and rather have the user reference the array directly. But the ‘added’ property is not necessary repeating current information because of the subsequent mutations. It would be a useful glimpse of the past similar to the removed property. Object.observe has a similar problem but less problematic in practice. If you change a property on an object being observed to a different value and then change it back, it would not be possible for the observer of the first notification to see what the property was changed to. A ‘newValue’ property, for the same reasons as above, would be a useful addition even though in many cases, not all, it is the same as could be ascertained by looking at the current object directly. Again I may be confused so any education would be appreciated. But it appears that this is a big information hole that would cause ChangeObservers to miss when elements get added to arrays which defeats the point of these ChangeRecords. I hope this was clear and useful. Any questions or clarifications on this explanation, let me know. Thx. Don Don Woodlock GM, Cardiology IT GE Healthcare IT T +1 847 277 5515<tel:%2B1%20847%20277%205515> C +1 781 608 7743<tel:%2B1%20781%20608%207743> don.woodlock at ge.com<mailto:don.woodlock at ge.com> www.gehealthcare.com<http://www.gehealthcare.com/> 540 W. Northwest Hwy Barrington IL 60010 GE Imagination at work -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20140222/57e4a791/attachment-0001.html>
On Sun, Feb 16, 2014 at 5:25 AM, Woodlock, Don (GE Healthcare) <Don.Woodlock at med.ge.com> wrote:
Thanks for the thoughtful feedback. I'm very glad you're excited about the feature. The short answer to your question is: the data you want is provided, but your processing needs to be more sophisticated.
[BTW, The Chrome/V8 implementation (to the best of my knowledge) fully implements the latest spec for Object.observe]
Basically, you're making an assumption about processing log data which is understandable, but unfortunately false. Though it would be nice, it's simply not possible to process each change record by looking at the final state of the observed object.
Object.observe provides full information about the changes that occur, but depending on what view you want, you may need to do some processing to get the right answer.
Based on the use-case you describe, it sounds like you have a very common desire -- to synchronize two objects, one of which has changed. The pattern requires what I'll call a "diff in time" view of the changes. In other words: from time 0 to time 1, what's the minimal set of changes I would need to apply to a copy of the observed object at time 0 in order to transform it into a copy of object at time 1?
As you've found, Object.observe doesn't provide you this view of things. It provides you with a log of what happened along the way. However, you can use the log view as input into a transform which will provide you with the diff view.
I've provided a JS library which serves multiple purposes:
-A handy library for those who want the "diff" view of things -A reference implementation of some of these algorithms -A polyfill so that you may use this functionality in browsers that don't implement Object.observe (the polyfil mode has less desirable performance -- specifically the cost to determine what has changed is proportional to the total set of observed objects -- as opposed to Object.observe where the cost is proportional to the number of changes which took place).
The library is here: Polymer/observe-js
It may be sufficient for your use case (as well as act as the polyfill you were attempting to create) or it may simply serve as documentation for the processing that is required.
Thanks again for your feedback and have fun.