 T Initially of this talk is a closer look at computer properties No pun intended in that, but I had soon done it anyway Before I start, let me tell you a little bit about my self I am Tom Clos on Twitter I think one of my aims in life is to one day be rich enough To purchase a second oak Anyone who follows me on Twitter Will know that I haven't really embraced Twitter As a medium of a communication yet But maybe one day I will I work for zesty.com, who is a start-up in San Francisco. We do food delivery, of course we're hiring. If you're interested in that, come and talk to me about that afterwards. I also convene Oxrug, which is the Oxford Ruby User Group, which meets on the third Thursday of each month. So if you ever find yourself in Oxford on the third Thursday of the month, come and say hello. This is a talk about computed properties. I know a lot of people here have done quite a bit of ember and know what computed properties are. For those of you who don't, I thought I'd start off by giving a quick recap of what computed properties are and what they do for you. Let's start with a really simple example. This is lifted almost exactly from the ember guide. Can everyone read that? Enough. We've got an object with two properties, first name and so name. Then we have a computed property name, which concatenates the two. We declare it as a property of your first name and the surname. That's how you declare a computed property. What does that actually do for you? In this slide, I've added a log statement to say every time that function is called. We can see what's going on and how this actually gets used. If I create a person, get the full name, the first time it calculates the name. It calls out function, puts those properties together, returns the name. If I do that again, it doesn't recalculate it. That's basically what computed properties do for you. If I change my name and call full name again, it does recalculate it. It does this intelligent like only recalculating a property if it really needs to be recalculated. Computed properties are an efficiency optimization. That's it. In any other framework, you might just call these functions. You can think about it as calling these functions, but behind the scenes, Ember is doing some clever stuff so that you don't have to call that function too often. Before we go on, I'm just going to take a quick aside about how they've actually done this. You'll notice that to declare that property, I actually called a function on JavaScript's function. Ember here has actually extended the function prototype in JavaScript. I did a little talk about this at Oxrug before I came. Some people got a bit upset about that. They thought people get a bit antsy when you start extending features of the language, these fundamental features that are called all over the place. Before I carry on, I thought I'd just say that even though Ember does this, this is more like syntactic sugar than anything else, and you can turn it off if you want. Ember has this saying you can set the environment variable, extend prototypes to false, and then it won't add that method to the function prototype. Your JavaScript remains clean, and you can declare computed properties like this instead. That dot property on function is, in fact, just a nicer way. It's just syntactic sugar for what's actually going on with the covers, which is this. You can find out more about that on the Ember site. That also applies to things like they extend the array prototype as well for various things. Most of what this talk's going to be about is how this stuff actually works on the covers, what is Ember doing so that it knows not to recompute your properties every time. We're going to be taking a look under the covers. At this point, you might be worried because one of the big philosophies of Ember is that you don't need to know what's going on underneath the covers. There's all this magic and you just trust it works, and most of the time it does. Don't feel worried. It's okay. Sometimes it's good to look underneath and see how these things actually work. Sometimes you're forced to do that, which is, in fact, how I started looking at this stuff, which I'll come on to a bit later. But it's actually quite interesting to see how computer science behind how they've implemented this stuff. As Jamie mentioned earlier, this is an interactive talk. It's kind of not that interactive. You don't really lose anything by not interacting. It was more that I was worried that people would get bored. At this link is the stuff that I used to. There's a very simple Ember app that you'll find here and a few functions I wrote. I wonder if I can actually bring this up and show it to you. There is it. There we go. Okay, so probably can't read that, but it's basically a really simple Ember app. At the bottom there are some utility functions which do things like print the metadata of an object, which is useful if you're trying to investigate what's actually happening when you're doing these computer properties. If you can get on the internet and want to play along, feel free to visit this URL. Okay, so let me just see if I can actually get this to work now. Okay, I think this is probably way too small for people. Okay, so one of the things that I define in the code is a person called Tom, which in fact you saw on the previous page. I've got a function called print meta on Tom. This shows various things that Tom has. These are all currently empty. Is that too small for people? Shall I increase it a bit? Is that a bit better? Okay, so basically each Ember object has a meta area. If I just type this in, you click on the class, there's this Ember meta thing here. What my meta function does is it just looks inside this meta part of each Ember object and prints out certain things that are going to be interesting to us in this talk. The thing is that at the beginning, if I do print meta Tom, it's all fairly empty. What I'm going to do now is I'm going to try and pull out my computed property. I have a computed property that is full name, which is exactly the same as you saw on the previous slide. It just takes your first name and your second name and it puts them together. Once you've done this, the meta changes. This is interesting from the start that this object is created. At the time it's created, it has the full name function. It has the first name, the second name, but its meta stuff is all empty. It's only when you first pull out the full, the computed property, that stuff in there gets populated. The next thing I'm going to do, I'm going to take... This is the meta from the previous slide. I'm now going to look what happens when we change one of the things that property relied on. I'm going to set the first name to Thomas, so that's changed. Then see what's happened. Basically, some values have been cleared out. Beforehand, we had cashed the value of the full name. The cash is now being cleared. There are these other bits. We've got this watching section that was one and one. They're now zero. We had this dependency section, which has also been knocked down to zero. Clearly something has happened here. We're going to dig into this a little bit deeper. This is a really simple example. We've got one property that depends on two other properties. I'm actually going to introduce a much more complicated example, which in fact makes it easier to see what's going on, because we've got a little bit more structure to play with. This is the updated code. We've got our full name, which depends on the first name and the last name. We've also got a first initial and a last initial, which does exactly what you think it does. It just pulls the first initial out of the first name and the first initial out of the last initial. It takes the first initial of both names. Then we've got initials, which is those initials taken together. Name with initials, which takes together your full name and your initials. Let me get back into this. I'm going to get that property, which was full name with initials. That's the thing that it returns. If I now print the meta for that, loads of stuff has happened. We've got loads of cashed values. We've cashed the value of each of the computed properties. It's now cashed in the cash. We've got certain values. We've got counts of things that we're watching. We've got dependencies. This dependency section is the bit I'm going to zoom in on, because that's actually the bit where most of this stuff is happening. That's the code. Let's focus in on the dependency section of the metadata for that object. We've got the first name, and it has first initial 1 and full name 1. What that is saying is that both the first initial and the full name depend on the first name. Similarly with the last name, the last initial and the full name depend on the last name. Then we have the initials depending on the first initial. The initials depending on the last initial, Name of the initial depending on the initial, and on the full name. So, what we actually have here is a dependency graph. That's basically what I just described. So, I've drawn the arrows maybe in a counterintuitive direction, but the arrow means I depend on something that I point to. So, this is the structure that Ember is storing in the metadata of that object. y graff dependence. Felly nid oes yn ddweud y rhai o'r ffordd cymdeithas. Felly, mae'n ddweud ei fod yn ddweud y ffordd cymdeithaeth, ond mae'n ddweud y ffordd cymdeithaeth yn ddweud. Felly mae'n ddweud y ffordd cymdeithaeth a'r rhai o'n ddweud y ffordd cymdeithaeth. is this. So almost everything goes to zero. Apart from this last name, last initial thing. So if you have, think about that for a minute, you can probably kind of see what's going on. So I've invalidated by changing the first name, I've invalidated that part of the ac yma, yr ysgrifatau hyn yn ei ddau'r yn ein gwestiwn yn dde-nishirio arall. Yn hoffi'r hoffa ac arall, mae'n stîl am y'r hoffa cwrdd yr hyffa cwrdd yn ei ddweud alsydoedd yn ysgrifatau. Ryth i'r hoffa dros yma, mae'r hoffa dros yma rwy'n nhw fath o gyfliad o'ch fathau allan, ddyn nhw'n fath o'r hoffa, ond mae'r hoffa'n ddwy agnoredu'r hoffa paddydd i'r hoffa peth o'r hoffa, a'r unig bod yn gyd-doedd ymlaen nhw'n gyd-doedd i'n gwasanaeth. Cheddiwch ar wahanol, mae'r hyn oedd oedden nhw'n gwasanaeth. Rwy'n gofio'r gwaith yn eu gwirionedd yn fawr. Rwy'n gwybod oedden nhw'n gweithio'n gwirionedd y mae'n gweithio'r gwaith. A dwi'n cael ei wahanol, rwy'n cael ei fawr oedd eich gwaith yn cael ei wahanol. A felly, iddiwch i'n gwaith ar y fawr yn ôl. If I were now to call full name with initials or name with initials or whatever it was again, that graph would be rebuilt up with a different set of dependencies there, not set of dependencies but different values at each point. This is kind of important. Don't recalculate the values, just invalidate the cache. So it's like Ember trying to do the minimum amount of work possible. If it recalculated everything at each point it would end up calling these functions where maybe in fact that value would never be used. So it doesn't recalculate them until it needs to and then it pulls everything back through again. That's also interesting because in fact Ember did something a little bit stupid here, not really a little bit stupid but like it could have done better because my first name changed from Tom to Thomas and so really the first initial didn't change and it invalidated all that part of the graph including the first initial and everything that depended on it but that's clearly like a trade-off that they've thought about. To know that the first initial hadn't changed they would have needed to recall that function and while that would have saved us invalidating that part of the graph in order to actually make that optimization you've got to call these functions more often. So they've clearly decided not to do that. So this is kind of how it's working. There's some cash values, there's this dependency graph and through the dependency graph they know which cash values to get rid of when something changes. Now I like to think about how that actually works because clearly one property has changed and that has somehow triggered this graph to be like invalidated and there are sort of two ways that could happen. Either there's something sitting there watching that value all the time saying has it changed, has it changed, has it changed and the moment it changes it like wipes everything out or that thing when it changed somehow triggered the whole collapse of all those properties. So this was something like that kind of surprised me quite a bit. So if I refresh this, if I take out Tom again so at this point I can do Tom dot first name equals Thomas. So this is just like a JavaScript object you can set properties like that on a JavaScript object so I can now do Tom dot get full name and it tells me it's Tom is close. I can now try and set it to Tom and it gives me an error. So that's kind of weird. So the act of like requesting the full name property has actually changed stuff on my object so that the way of setting things that worked originally doesn't work anymore and in some ways it has to work like that because these JavaScript objects you don't even need to declare the properties before you use them but it's also kind of weird that this passive getting of a property has fundamentally changed the object that I've been working on and you can actually see the changes it's made. So if I refresh again and put in Tom if you look in this class it's kind of empty there's not a lot of stuff in there if I then do Tom dot get full name and then type Tom again and have another look inside you'll see now that we've got getters and setters for that first name and the last name property so just requesting that value has created these getter and setter methods which means that if I now try to set that property directly Ember now complains it wants to force me to do Tom dot set first name to be Tomas and the reason it wants to force me to do that is because by using its set method that's the thing that triggers this graph to collapse if I'm forced to go through its setters it then knows what to do so that's just recapping on what we've just found so that's kind of weird and interesting and kind of nice in a way the second part is like how do those setters actually know when they need to invalidate a load of graph and the answer to that is a bit of the meta information that we kind of skipped over before and it's the watching part so let me just pull up that figure that we had before so when I had my blank object and I looked at its meta it was all empty and then the moment I got the full name on it I then had all these properties filled in and in particular I have this watching section and that is what the setter checks to find out if it needs to do more stuff if this watching thing was zero it would quite happily just go along and change that value and not bother with the graph at all but because it knows this full name depends on something and I've actually got a cached version of the full name depending on that version of my first name it's put this watching count of the first name to one which means that when I try and set that first name it knows that something is watching it so it calls into these other functions which will then invalidate that graph this is quite interesting so getting a computed property actually does a lot of stuff so to start with it defines a new getter and setter on that object each of the properties that that computed property depends on so it modifies things that in fact on the face of it are nothing to do with it at all that setter knows to check whether that property is being watched or not and if it is it will kind of notify the watchers or like do stuff based on the fact that it's being watched so that all sort of makes sense and so we've got like this dependency graph we've got things being watched we've got new methods defined when you like pull out these computed properties is that it? and this would be it if we were only ever dealing with a single object so things get a lot more complicated when you want to do things like this so this is one of the great things about input properties you can change them so if I have my person and my person has a location and I want to know the person's like location name so like the name of their location I can define a property like this so I can define location name on my person and that's a function which gets the name of their location and I can declare that as a property with a path so like it's not just a single string it's got like this dot in it and then this is just like setting up this object so I've got this place called I've got this location that represents Oxford and I've got this variable Tom that represents a person with the location Oxford so if I now call Tom get location name let's just have a look at what it does so if I do that and then print the meta information this is what I come out with and it's kind of weird because I've got the dependency graph as you kind of expect I've got that key location dot name that I specified and it's told me that location name depends on that so this is like my little graph I've got location name depending on location dot name location dot name seems to be like a virtual property of this object but then the other stuff doesn't really make as much sense so watching one thing is watching location name location dot name which makes sense because that's location name is watching it but also something is watching location and there's no indication as to what that thing is but there must be something because like the location name there are kind of two ways that the location name can change I could either swap in a different location or I could change the name on the location that already exists so it has to like account for these two different things so what I've actually got is two completely separate objects which is why the thing that we were doing before broke down like before we had this one object and that whole dependency graph was stored in the meta information for that object I've now got two completely distinct objects which don't have like shared meta information and so kind of this is the situation at the moment so what I actually need to do is we're not looking at enough of the meta information so in the print meta information it actually also prints these two extra sections chain watches and chains and this is where it gets really exciting and really confusing like if you try like just reading the source code for this stuff it's like practically impenetrable but if you actually like play around with a bit you can kind of see what's going on so to sort of like answer the question before like what is watching the location the thing that is watching the location is a chain watcher whatever that means so this actually kind of helps explain what's going on a little bit so when we pull out that property location name instead of just like setting up a dependency graph ember sets up a chain to watch each thing that's important in that path so it sets up a route which is kind of linked to Tom so this chain is owned by the Tom object because it's relevant to the Tom object it's kind of rooted in the Tom object and then the best way of reading this is kind of like Tom's location is the Oxford object Oxford's name is the Oxford string so that's kind of how you read this this diagram so that's kind of what it so this is kind of this is kind of a fairly interesting sort of structure and I encourage you to like spend some time thinking about why this is the thing that they've done and why this is the thing that works and I haven't like fully got to the like I can after looking at it for a couple of days I kind of vaguely understand sort of why this would be the thing and why nothing else would do but it's kind of interesting that this chain like sort of winds round all these objects and yet it is rooted in one place and the different nodes of the chain know about those things and you can also I won't talk about that you can have like chains that branch but they all have to be rooted in that one object so probably a good thing to look at is what actually happens if I change a property of part of that chain so I've abbreviated the name here I've gone in and I've set that location so I've like in the terminal I've gone Oxford dot set name comma Oxford so the way this kind of works is this Oxford object knows that something is watching its name property so when you set name on that it like goes into the metro information it looks at something that's watching it looks in the watching section it knows that something's watching it and so it then goes to like an internal thing in its dependency graph it finds out that it isn't so it then goes and checks in the chains and it realises that this chain is watching it so it kind of communicates that information back to the first node in the chain then that chain communicates it back up to the second thing in the chain which then communicates it back to the root and when it gets there the root like tears down the whole chain and again this is kind of interesting it doesn't like update the chain in any way it just like throws it away just tears it down much like it threw away part of the graph when it wasn't relevant anymore so that's what happens if like the thing on the end changes if a similar thing happens so this time the way I've done this is I've gone to the object Tom in the console and I've swapped out Oxford for a different location so I've swapped out Oxford for the location San Francisco so again like at this point it's Tom that realises so I've called set location to San Francisco on the Tom object so it's the thing that like it looks in its thing it sees that something was watching its location it's not sure what that is so it looks in its chain watch as it finds out that that's that chain so it communicates that back to the location part node of that chain that like I've changed then that sends that down to the root and similarly to before the root like destroys the whole chain and I say it destroys the whole chain so in the previous case where it just it was just the name that changed it might not have you might have thought it was like a bit wasteful to destroy that whole chain because the location there could have been other things that depended on the location like location might have like coordinates or something which might not have changed with the name and this is where ember uses that like count of what things are watching so if there were still some things watching so if like if this thing had been a branch chain so off location it would have also been watching like coordinates or something it wouldn't have torn down the whole chain it would have just torn down the bit that had changed so it's pretty clever so the reason I kind of started doing this stuff was because I got really confused about how ember was doing like the array computed properties so if you do something.each.something and in fact that hasn't made it into this talk it's still something that I'm so I think the way that works so I've got an example here so you can do Tom.get hobby names tells you some hobbies I can then print the meta on Tom and you can see it's like a similar sort of thing like I've got these chains and just one of the nodes in the chains is just like this each node which is like a separate object which presumably represents the array of things it then got pretty weird because if you actually look in things like if you look at the meta of either of the hobbies there are things watching them like it knows that some things are watching the name but there's nothing to say what's watching it so I'm still slightly confused about this stuff but it's also kind of gets out so all the stuff I talk about so far is in one like module of ember which is this so the ember metal the ember metal like module and these are like some of these are kind of like the relevant files and how they relate to each other and by relate to each other I mean like which things so it's all done by ES6 modules and it's like which modules like import stuff from other modules so it's not like necessarily the best way to look at these things but it gives you an idea of kind of what's going on and how these things depend on each other the stuff with the so I don't know if you know but like ember now has array computed properties which so if you use an array computed property like for example you can do an array dot map so you have an array of things say you have an array of numbers and you want your computed array to be twice each of those numbers so you can do that with computed properties and each time one of the numbers changed ember would like re-compute the whole array whereas array computed properties are an optimization on that whereas if the whole array changes it only re-computes the element that's changed so it does really clever things like it it knows that if you remove the third element it just like it just removes the third element in its computed array it does all that stuff and that's really what I was hoping one day to like get into how this stuff really works em as far as I can make out that each thing was like observing it's things that are in its array and then I kind of run out of time but em this stuff is really interesting that's pretty much all I'm going to say about it I encourage you to have a look at these files because the way this stuff works under the covers is like once you've got this like vague overview of like what it's implementing the actual code that they've used to do it is really nice em so yeah em I guess that's about all I'm going to stop talking now probably not soon enough but em if anyone has any questions or anything be happy to answer them the reason why it's doing it's destroying tearing down the cache I'm assuming with the chain it's tearing down the cache and not destroying the chain is it for just performance reasons as opposed to any other reason so I think it actually does get rid of the chain it doesn't rebuild it when you recalculate the property so I don't think it's particularly destroying the chain because it's like taking up memory or something I think it's just like this stuff is useless to me now I might as well get rid of it em and I'm not going to recalculate it until I'm really sure I need that recalculated property because it might just be wasted effort yeah it's just being lazy rather than anything else I think so are em I take you look at the properties and property observers are actually two separate concerns in embers of human world I think so yeah so it could be that so I've left out this portion of this graph that corresponds to observers because I didn't really go into that that much but it could be that those observers are the things that would explain why there are things that are watching but nothing in like the dependency graph chain watches or anything it could be I mean there are other bits of the meta information that I didn't look at that much so that could be that could be how the observing stuff works I'm not that's just a guess so we're two months away we're saying time close is still close but still no scar yeah so thank you one more round of applause