Principles of Dynamic Interfaces: Live with the R&D team

Lou d'Andrea from Wolfram Research explains how to build responsive dynamic interfaces in the Wolfram Language. Key principles: use Dynamic and DynamicModule to track dependencies, push dynamics to the lowest option level to minimize reevaluation, and control update frequency with TrackSymbols and UpdateInterval. The Q&A covers debugging tools like Link Snooper and strategies for long-running tasks.
Chapters
Okay. Hello everyone. My name is Lou d' Andrea and I work in the User Interface group at Wolfram Research. I'm a senior user interface developer and today I'll be talking to you about some of the things that we've learned over the last, I don't know, 15, 16 years about building dynamic user interfaces in the Wolfram language. So we, I will, I will go over the, I will list some of the main principles that we've discovered and I will dig into each of them a little bit, giving some, some of the, the back history, giving some of the, the sort of rudiments and also advanced tips along the way. So hopefully this will be useful. So these are the principles that we've discovered. Principles of programmatic generation, minimal dynamics, maximum encapsulation and evaluation, blocking. It's important to understand each of these when building interfaces in the Wolfram language. And I'll talk about each of them. So let's start with the principle of programmatic generation. Every interface should be generated by a program. Well, that's sort of obvious. What is there to talk about here? Well, let's talk about the language and what the language supports as far as the primitives that you can use when building these things. So if you're aware at all of interfaces in language, you've probably been exposed to things like dynamic, dynamic, dynamic wrapper, dynamic module. I'll talk about each of these in a little bit. But these are sort of the fundamental building blocks. There's some deep technology here that lets us do some really interesting things with interfaces. Then we have a set of user interface controls, button, checkbox, slider, all the controls that you're used, accustomed to from, you know, web pages or apps. Wolfram language has some, some controls that are unique to it or that you might not find in other languages. We have, you know, low level event support. We have the ability to do direct manipulation via locator pane and so on. So this is sort of a, giving you a flavor of the kinds of language primitives that there are for dynamics and controls. There are also lots more information. I mean all this is documented, lots more information on the linked guide pages. I encourage you to grab this notebook once it's available. So that's dynamic and controls then building out interfaces. Of course it's important to be able to lay things out nicely. We have the ability to style and frame stuff. We have 1D and 2D layout constructs. We have constructs that allow one to assemble multiple expressions into the same region, whether it's showing one at a time or you know, in sort of common user interface constructs. And again, all these things are documented, but just to give you the flavor of what Wolfram language supports. And then once you've built your thing, question is, how do you get it in front of other people? Well, there are, there are language design things for that as well. We have deploy and cloud. Deploy to getting things, you know, in a state where they are ready to be used either on desktop or on cloud. We have ways to take a self contained interface and deploy it to a document window, a palette window, or even into a piece of a window. So in a contextual interface or a toolbar location, okay, so those are sort of the, the language primitives. And then there's a couple of different ways that one can generate, one can think about generating language, generating these, these interfaces. And there are essentially two ways. One is to generate them the instant that the user requests that they be generated. So manipulate, for those of you who know the language, manipulate is a funct is whose syntax is as simple as table, sorry, with a new keyboard here. So say you just want to explore some expression over some domain and it instantly produces an interface. The point I'm making here is that this, this interface did not exist before you asked for it. Manipulate goes off and, you know, studies the input, takes a look at what controls I think would be useful in this case. It's clearly, you know, you're defining a, a continuous range. So a slider like control would be most useful. So manipulate is an example of something that's generated on demand. Same for system information. The WolframAlpha function. Audio and video outputs. So we have audio players and video players that are built on demand in response to inputs and other, you know, lots of other things. Is this is not an exhaustive list, but this is so generated on demand. And then there are, there are a large number of primarily palettes and dialogues that we generate that we generate during what we're building Mathematica as a product. So when we build the about box, this is generated by Mathematica code. This is not sort of hand edited, you know, the bits and pieces of it might be hand edited in their own notebooks. And then the assembly of the about box happens, you know, by, by that that's managed by code. The same for the typesetting palettes, the assistant palettes. And the same is true for the preferences, dialog, other dialogues, all of the toolbars that we have in product, most of the contextual interfaces. So, so when considering, you know, what kind of interface you're providing. It's important, you know, if you're going to be including this interface in some way and say dropping something into the packlet repository, you might want to have a packlet that has the code for generating the interface, or you might want to have a packlet that contains pre generated interfaces and how those, how you deal with each of those is going to be slightly different. Now let's talk about a little bit about, just to make sure we're all on the same page with regard to what this dynamic thing is that I keep mentioning. So dynamic of, of an argument is, you know, one of the fundamental building blocks of interfaces. And in short, if you evaluate dynamic of expert, the output of that should display the value of expert as it is at that instant in time. So, and time keeps moving and so the expert keeps, might keep moving. Here we have an output who's, you know, shows us that X does not yet have a value, but if we give X a value by evaluating this input, this previous output, the display of it will change to reflect the current new value of X. And here, so here we have a slider that adjusts the value of X in real time and we see that just by virtue of referring to the same global variable, variable, you know, this slider changes the value of X and dynamic of X shows you the current, you know, the, the up to date current value of X. Okay, how does this work? Well, in a nutshell, whenever the kernel evaluates dynamic of expression, this case dynamic of X, it computes a the full dependency graph to note all of the symbols, all of the Wolfram language symbols that were involved in figuring out the value of this variable. In this case it's just the value, just the variable X. But that could be arbitrarily more complicated. So here's an example that shows, you know, so we're going to have dynamic of F. F is a function defined to be the sum of another function and some global symbol G itself is defined to be some other function, some other global symbol. So here we have dynamic offof3. If an unrelated symbol gets a new value, then this dynamic does not change, does not be up, it's not updated. But if, if one of the values, if one of the variables on which it depends changes, I evaluate A equals 2. We see that the output has changed accordingly and we can even, you know, redefine one of the utility functions here. And if we do that, we see that the value of that dynamic has changed. So the kernel sort of has deep knowledge about which symbols are involved in updating and keeping this dynamic up to date. So that's useful. Sometimes it's a little bit too useful. It can result in dynamics that update too frequently. And by that I mean a dynamic. So here's an example of a dynamic which merely increments the value of C. So dynamic of pre increment of C. This didn't just increment C once, it keeps going. Why is that? Well, because during evaluation of this dynamic, the kernel noticed that it depends on the value of C. Fine. So whenever the value of C changes, this dynamic needs to be updated. But evaluating this dynamic payload changes the value of C. And so value of C change. The dynamic needs to update again. And during that update the value of C changes again. And thus, you know, we have this, we have this constant re triggering of dynamics of this particular dynamic. Sometimes this is useful when you want to, you know, have a self driven animation when you want to, you know, have some model that changes on its own, at its own pace, not driven by an animator. This can be useful. Sometimes it's not so useful. And how to avoid this? Well, there's a couple of options to dynamic that come in handy. You can exclude symbols from the dependency graph with the track symbols option. So in this case we're saying throw away all of the symbol triggers that would update this dynamic and instead rely on a time based trigger. So update interval specifies a maximum update interval. So if a second has passed without this dynamic being updated, then update interval will, will cause it to become updated. And here we see that we've now successfully slowed down the updates on C. Okay, so that's when updates are too frequent. The opposite can happen too if they're too rare and track symbols is not additive. So you can't use track symbols quite as packaged to add sort of an arbitrary dependence to a expression. So here we have. I'm going to delete this other output that's changing value of C. Here we have an output that displays the current time and date and track symbols C. However, if I were to change the value of C, notice that this time output is not updating. Why is that? I set track symbols to see. Well, because this output does not depend on C and track symbols doesn't add dependencies, it only removes them. So if this output depended on C, for instance, you know, if we have, oops, if we have C now, you know, being a critical part of the dynamic payload, then when updating this we see that I should have put C last. We see that the time is in fact changing in that. In that date object as I'm changing C. Ok, so that's track symbols and update interval. Similarly, you know, as we've already said, it provides a sort of maximum interval after which an update is forced. So by default, data objects do not trigger are not dynamically ticklish, they do not trigger dynamic updates. But if you wanted to effectively have some sort of clock, you could say, well, update this data object every second. And we see here it updating. So some possibly useful things about dynamic. One other thing I'll mention about dynamic is that it really wants to be seen. By that I mean if a dynamic output is not displayed, then its payload is not evaluated. If it's not displayed in a typeset and rendered by the front end, it's not going to evaluate. So here we have, you know, so let's see. Var does not start with a value dynamic var =4,5,6 semicolon. So the output is suppressed evaluated it. Does var have a value? No, because this dynamic, you know, dynamic has the hold first attribute and it the only way that its contents will be evaluated is if the output is allowed to display. So here's dynamic var just to drive the point home. And the lack of display doesn't just extend to semicolon, but if you have a dynamic. So this is some code that generates a new document with a closed cell group and in that closed group is an ex is a dynamic that sets the value of this variable. So even displaying this new notebook does not change the value of the variable because that dynamic has not yet been displayed by the front end. So if I open this, if I open this section, we see that that dynamic has triggered and in turn has given the value of var has given var a value and that value is now reflected in the dynamic update dynamic output. I will from time to time glance over at the at questions that are being fed through from you all. There's a question, is there any way to see the dependency graph underlying a dynamic evaluation? And the answer to that is no, we have not built that out. It would be in principle possible to build that out, but it is not something that we've done. Okay, so dynamic wants to be seen. And while we're talking about the display of things, I wanted to mention another useful thing to know about the language design of of some of these constructs. So there are constructs that allow you to specify multiple expressions at the same time. A pane selector mouse over tab view display one expression at a time. Open review allows you. I'm Sorry. Each of these displays only one at a time. Overlay is another one that allows you to specify multiples at a time. And the point I want to make here is, by default, these structures need to know the. In order to determine their height and width, by default, they need to know the width of the widest element and the height of the tallest element. So here we have, you know, a tab view that shows various powers of 99. And notice that right up front, when we first evaluated this, it left exactly the right space for the largest pane. How did it do that? Well, it did that because image size, one of the options of tab view, is set to care about the size of all of its contents. However, when you are dealing with interfaces where your panes may contain dynamics, those dynamics may be long running and maybe you don't want them to evaluate until they're actually displayed. So here I've added some echoes just to. So that we can see when these dynamics are updating the tab view with a couple of tabs, evaluate this and those echoes are going to appear in the. I think they're going to appear in the messages window, right? So maybe I'll. So let's see that again. If I evaluate this tab view, not only does the tab that's being displayed evaluate, but the tab that's not yet being displayed also evaluates. And if you. One can imagine, you know, if this tab contained, you know, lots of complicated things that took several seconds, you wouldn't want the display of the first tab to have to wait for it. The only reason that's happening is because the tab view is trying very hard to determine its default size, which is the height of the tallest item and the width of the widest item. So to avoid this, you can use a setting for image size. The only difference between this and this is the image size setting. Image size automatic means don't look at all the tabs, don't look at all the panes, only look at the one that you're. That you're. That you're displaying right now for sizing. So now we see we evaluate this tab view, only the first pane's dynamic is triggered. And when we switch, it's only when switching to the second pane that the second one triggers. Okay, there's lots of questions in the chat. Maybe I will continue with my talk and then circle back. I think there'll be plenty of time for questions. But rather than divide up, rather than pause for each of these, let me defer those until after the talk. After my Prepared remarks, I should say. Okay, so on to the next principle, the principle of minimal dynamics, where, which states the dynamic should never encompass unnecessary evaluation. It should be clear what this means. But let me give you an example from real life, so to speak, where, you know, one thinks of, you know, dynamic. We want something to happen, we want something to reflect the value of some symbol. So here we have a checkbox that changes the value of a variable between true and false. And we have a dynamic that looks like it does some sort of ND solve thing. By the way, this is, oops, this is an iconized input. I can talk about iconized inputs if people like. So we have an ND solve that takes some time and then a density plot that takes some time. And one of the options for that density plot is the thing that you're setting by this checkbox, whether or not there's a frame. So we evaluate this and you know, it takes as long as it takes. And notice that toggling this checkbox is not quite instantaneous. The reason for that is the dynamic. When you toggle this checkbox, the dynamic needs to reevaluate its entire payload. So that includes this ND solve which is not affected by the value of frame and includes all of the density plot, most of which is not affected by the value of frame. So how can we improve this? It should be fairly clear if I get rid of these guys, we push the dynamics so that it's only around the density plot. Well, that's somewhat of an improvement. The ND solve isn't changing. And if we evaluate this, we see that, you know, that's slightly more responsive, but we can do even better in this case because frame, the density plots, frame is something that the front end understands directly. So we can push the dynamic all the way into the right hand side of that option. So we have the Any solve is not being recalculated, density plot is not being recalculated. It's just the display or not of that frame. And now that's, you know, as efficient as it can be in the front end. Okay, so that's, you know, an example of taking a dynamic that's, that's encompassing unnecessary valuation and pushing it down to its lowest possible location. There's other things to keep in mind with dynamic and that is that they can be nested and nesting is something that we take great care to manage on the back end. So, and I've, I've stated sort of what the main, how to think about these. So if you have an inner Dynamic. Essentially this example is ex is just this example with some styling and echoes added. So we have dynamic of a list and in that list is dynamic of something else. So we have an outer dynamic and an inner dynamic. The same, same thing here, just with echoes and such. So when these dynamics are nested, an inner dynamic can update independently of any of its surroundings. Now this is effectively its own island. If the outer dynamic updates, there's anything that triggers an update to, to this expression, then all of that expression needs to be updated, needs to be reevaluated, including the inner dynamics. So I'm going to evaluate this. Again, we have some echoes. So we see that just evaluating them. Obviously the outer and inner dynamics need to update. So we have some sliders here that affect X to update the outer dynamic and Y affects the inner. If we change the outer dynamic, notice that in our messages log here both the outer and inner dynamics are being updated not because it, not because Y is changing, but just because X is changing. But if Y needed to change independently, there's no reason for that outer dynamic to update. We have, you know, so see in the, in the log only the inner dynamics are updated when Y is changing. So this is, this is an important thing to keep in mind when nesting dynamics. And I'll mention just for the record that if you work with manipulate, manipulate has effectively its own dynamic that it will wrap around the content of, around its first argument. So if you had a situation where you wanted an inner dynamic or as the previous slide, you want to sort of push dynamics down to a lower level, you know, you would, you could still do that by referring to dynamics within the first argument. Right. So et cetera. Yeah. Okay. Just something to keep in mind for those of you who like manipulate. Ok, so minimal dynamics. Sometimes minimal can mean zero. Sometimes it's important to consider whether the task that you're trying to accomplish is not in fact a fundamentally dynamic task. Maybe it is just something that can be done as sort of a one off notebook programming operation. Notebooks are structured documents been around for 30 years, 20 odd years. And there's a rich language of doing notebook manipulation, notebook programming based on them. Here is a relatively new thing that one can do. And with notebook programming you can have button and what that button does when you click it is it overwrites itself with the current time. So this will, these buttons will overwrite themselves with the current time. If I click the 2 button, the 2 button is now gone and it's been replaced by a data object. So I don't Consider this dynamic. But it's definitely a nice sort of notebook programming task. Here's another simple task. Say you want to grab all of the cells of a certain kind out of this document and create a new document. Well, we can do that with some simple notebook programming. Grabbing all the cells with a particular cell tag, reading them out of this document and dropping them into a new one. And here we, you know, we have the principles now. So. Right. I think that's useful and lots of other things that, that, that notebook programming people and this used to be the only game in town when it came to doing interesting things in notebooks. So we spent quite a lot of effort getting the, the set of notebook programming primitives figured out and working well. So I'd encourage you to consider avoiding dynamics when possible. All right, so that's minimum dynamics. Let's move on to the principle of maximum encapsulation. By this I mean that interfaces should not, should only do the things that they are designed to do and no more. And we've already talked about the two main ways that one can get to this state. We can have code that, that pre generates an interface, or we can have code that generates an interface on demand. And, but in either case, you will probably be using the same sorts of constructs and primarily you will probably, you'll almost certainly be using dynamic module. What is dynamic module? Dynamic module is a construct that we developed when we first were building out interfaces. And we, we realized that module, the kernel function module, it was not quite sufficient for interface construction. And the reason it was not enter it was not sufficient is that it does its localization. This is a lexical scoping construct and it localizes as the evaluation is progressing. It localizes in time, during the time of this evaluation. But this was not quite enough for interfaces. We wanted an interface. If you had an interface in your notebook and used copy and paste, for instance, to create a new interface, we wanted to make sure that that new interface was scoped entirely independent of the thing it was copied from. So the, the thing that we built to do that is dynamic module. And you can think of dynamic module as scoping in space, in the space of your notebook and scopes in a particular rectangle on the screen. So here we have, here we have a dynamic module. The syntax is very much like module. You have own values and a payload. But in this case your scope, you're creating a custom a, a scoping construct in which the value of x is local to this area on the screen. So if we, you know, clear the value of X. And we evaluate this dynamic module that gives X a local value and we've, you know, sliding it around with slider and dynamic. Neither of these is changing the global value of X. And it has the property that I described earlier. So if we made a copy of this interface initially, it has the same value as the local variable did in the thing we copied from, but merely copy and paste is now creating a brand new scoping construct that are different from one another and still different from the global value of X. And this is how for instance, manipulate does its localization. It relies critically on dynamic module, not just for this, but for some other things like saving the state of local variables. If we were to save this notebook now, these sliders, when reopening the notebook, these sliders would have, would remember the values of these variables and likewise for manipulate, would remember the value of that variable. And the way it does that is dynamic module does this for free. I mean, it was a lot of effort, but the result of that effort is dynamic module does this for free. Other things that dynamic module provides is a way as a place to put code that you might need to bootstrap your interfaces. You might need to, you know, to, to, to set up the state that your interface needs, the kernel state. So here we have an example where we have an initialization option that, that literally sets the value of some function and then that function can be used in, in the dynamic display. This is a silly little academic example. Forgive me, but hopefully it demonstrates the, the, the idea. More commonly, if you're going to be deploying complicated interfaces that depend on large bodies of kernel code outside of the dynamic module itself, you might be using idioms like this where you, you set the initialization to be, you know, go grab my package and load it if it's not loaded already. And then you can use, you know, your package functions within that dynamic module. One nicety that we've built into manipulate and into dynamic module is if you have a, a, if you have a dynamic module and you don't want, you know, it depends on lots of local state. Let's say you're, you're, you're iteratively building some interface in your notebook and you have some function definitions that are sort of littered throughout your notebook, maybe several notebooks, and you just want to build an interface from them and to have that interface go off and slurp in all the definitions of all the relevant functions in this, into this dynamic module, into its initialization. And we have built an option that does exactly this. So save definitions. True. As an option to manipulate our dynamic module will go through all of the rest of the arguments and options to dynamic module, determine which functions are there, which symbols are there, and it essentially calls full definition on every one of those and includes the result as an initialization to that dynamic module. Right. Okay, moving on. Other interesting, useful, common, gotcha kinds of things about dynamic module. I thought this was definitely worth mentioning. This is sort of the first mistake that most people make when dealing with dynamic module, and that is to remember that it's a scoping construct, just like module. So if you want to build a dynamic module, you know, if you say you want to build a complicated interface, you don't want to have this interface be all defined in a single enormous definition for dynamic module. You'd like to separate it into, you know, different cells, different definitions, different packages, possibly. So here we have, you know, the simple everything in, in a single dynamic module. But how do we split something like this up? So more maintainable, Possibly we have, instead of embedding the controls and the dynamic display directly into the dynamic module, we can, you know, have utility functions that create them. Here's the utilities that create them. Seems like this should work. Doesn't quite work, though. I think we can ignore those. Doesn't quite work in the sense that this slider doesn't work. Well, why not? Controls. Controls, Display. Display. What's going wrong here? Well, what's going wrong is that when calling controls of a, controls does not have any holding attributes. And so what's being passed as the argument is not the symbol A, it's the value of the symbol A. So the call here is controls of A's default is one by default. So controls of one. So one slider dynamic of one. And that's where these set object messages are coming from. Because slider of dynamic of one is a meaningless, meaningless expression. And so we're, we're passing these variables by value in this case, which is the wrong thing to do in dynamic module. We want to pass them by reference. A couple ways to do that. One is to wrap them. Wrap each such. Each such symbol that you want to pass by reference in a holding construct like dynamic. This is sometimes nice because there's a. There's a. This looks a lot like the slider syntax. Maybe this is, Maybe this is syntactically useful. I don't know where those are coming from, but I'm going to continue to ignore them for now. So, so by wrapping these guys and holding constructs and teaching the Utilities about that wrapping, we can get a usable thing. Likewise, you know what I am going to. But likewise, here we have, here we have another dynamic. And notice that because of save definitions, true, the definitions of these new utilities which were evaluated with holding attributes are embedded in this output. And so even though I quit the kernel and even though, you know, I have not yet evaluated n of 1, this, this, this interface now springs back to life. So that's kind of nice. Okay, so that is the first few principles. The last principle is the principle of evaluation blocking. I'm going to spend a little bit more time talking about this than the others because it's got some important things to consider. So before I convince you that there's something interesting here, let me show you that there's something interesting. So we have here a manipulate and we have an infinite evaluation that is completely blocking the evaluation queue. However, even though the kernel is completely busy servicing this evaluation, we are still able to interact with the kernel, with that same kernel via this interface. How is that possible? What is actually happening here? Seems like the kernel is responding to two different things at the same time. That's not quite true, but it's mostly true. Let me describe the situation with an analogy. So imagine you're in a grocery store, another example of queues. You're in a grocery store that has two checkout lanes. It has an express lane. It's not self serve. This is old school express lane that has a cap of, of six items. You have six or fewer items, you can go through the express lane. If you have more, you go through the main checkout lane. Or even if you have less, you can go through the main checkout lane. Those are the two, the two lanes. Okay, now say there's only one employee working at the cash registers. I'm going to call that person Cornell. But Cornell is staffing both of these lanes according to some rules. And the rules are if there's nobody in the express lane, then Cornell takes care of whoever's in the main checkout lane. If there are customers in the express lane, Cornell pauses work on the main checkout lane long enough to service the express lane and then continues working on, on the main checkout lane. And finally, if the customer in the express lane, you know, violates the item limit, if they have more than six items, Cornell will just stop checking them out. You know, unlike my local grocery store, they actually, Cornell actually enforces the, the item limit. So this I think is a remarkably good, not perfect, but remarkably good analogy to understand what's happening when the kernel is servicing dynamic evaluations versus Shift enter evaluations. So in the case of dynamic, by default, dynamic of expression is a synchronous operation. It only runs for a few seconds before it will timeout. And while it's running for those few seconds, the front end stops what it's doing and services just that update. So the expectation is that dynamic of expression is going to be fast by default. If it's not going to be fast, there's a way to say, well, this dynamic isn't supposed to be fast. This can use the regular queued evaluation mechanism. So dynamic with synchronous updating false can take as long as it wants. The front end is not going to pause, wait for it. It's going to treat it just like any other shift enter long running evaluation. In fact, it uses the same evaluation queue as shift enter does. So these are two sort of fundamentally different kinds of dynamic. And there's other operations, there's button operations can use either a slow path or a fast path. And we'll see a couple of others in a few minutes. But I wanted to at least put some words into your brain that all sort of mean the same thing or similar thing. So for the slow, for the, the, the. The main checkout lane, the lane where you can have as many items as you like. Sometimes we call those in Wolfram language, sometimes we call it the main link. You might hear people refer to queued evaluations or asynchronous dynamics. In some controls you might see options like method queued, synchronous updating false as above, synchronous initialization false, which is supported in Dynamic module and some other things we haven't talked about yet. Control active and performance goal. We will talk about these in a few minutes. And on the other side we have the fast link. The fast, the system that expects its evaluations to be fast and is optimized for that case. Sometimes you'll hear talk about the preemptive link or preemptive evaluation, Synchronous dynamics. In button and Event handler and action menu you might see method preemptive. This is the default for those controls. Synchronous updating true is the default for dynamic and so on. So often you'll hear these things sort of mixed together. It's not word salad if you sort of keep these, these categories, you know, sort of separated in your brain. I think it's not, it's not rocket science, but if you have six more, if you have more than six items, if you, if you have a slow Running thing. Don't try to get through the express lane because it's going to cause problems. Don't try to do it in a dynamic in a synchronous dynamic. And we'll see some examples of how to deal with that in just a minute. So letting slow things be slow, what is the limit and how slow is too slow? Well, the limit by default is around six seconds. I would encourage you not to go hunting for the option that changes that limit because it was set that way for a reason. But here are some examples of things that that artificially might take a while. So here we have a dynamic that whose payload takes longer than a few seconds to run. And if we allow that to happen, then synchronous updating false. Will allow that to happen. What the way the front end handles I.e. it reserves a place for the output once it comes back, and then when it does come back, it fills in that space with the appropriate output, which for pause is just the output is null. Here is another example, this time of a synchronous initialization. We have a dynamic module who displays the value of some variable and the initialization goes through a lot of effort and then changes the value of the variable. And we saw that zero just now turned to a one ten seconds later without synchronous initialization false. This initialization function would have expression would have aborted after six seconds or so. Similarly for this dynamic, okay, what other things might be slow? Well, anytime you ask the user for input, you have to assume that the user is not going to be right on top of things and they might be taking their time with some sub dialogue. So here we have an input request that blocks in the kernel. And the only way that this can work when called from a button is to set that button to be a queued button. So notice I click that button, the output cell highlighted as if it were evaluating, because it is the button's function is evaluating. And now you know, the user can take as long as they want. Interacting with whatever these are and finding just the right color for whatever and then saying okay and no aborts, that all work fine. Anytime you're hitting the network, you know, you don't know if somebody's going to be an airport or a hotel with lousy connectivity. So I'd encourage you for any network transactions to consider using the slow lane. Anytime you have complicated box structure or you're assembling some big report that you know, you don't know how long it might take, use the slow lane. There's no, you know, there, there's no shame in using the slow lane. Some things just take a while. But it would be nice to show your users what's happening during that case. During, you know, during a slow, slow running evaluation, a long running evaluation. And there are some primitives we've built into the language that can do that. Outside of interfaces, we've built something that says, well, evaluate this, and while it's evaluating, show me dynamically the updated value of this other expression. So here we have a monitor that does, that runs through a do simple do loop and it's just showing the value of the, of the, of the iteration variable and the display that the second argument of monitor can be arbitrary. We have, we have, wow, that was fast. I'll do that again. We have primitives that show progress indication, definite progress indication. We have indefinite progress indication that just says, you know, please wait. So how is this really happening? How is this really working? Well, it's working a lot like the while true example we saw before. We have a synchronous dynamic and we have a queued evaluation, and the queued evaluation is causing the synchronous dynamic to update. So here is another example of a button which when clicked, kicks off a queued evaluation of a do loop and then displays that. So instead of monitor, we can effectively get the same sort of stuff from button clicks. And just to drive the point home method queued here is not optional. If buttons are preemptive, buttons are preemptive by default, dynamics are synchronous by default. And so clicking this button will pause everything in the front end until the button function is completed. Preemptive evaluations cannot preempt other preemptive evaluations. I'll say that one more time. Preemptive evaluations cannot preempt, cannot interrupt other preemptive evaluations. And I'll point you at some useful documentation of that in a few minutes. So building out on the button example a bit more, here we have a button that shows and goes into a running state in order to do something and it shows a progress indication and a percent finished while it's running. So yeah, that's nice. Okay, good. We can do indeterminate progress indicators as well. But I did want to point out one thing about the code. As these code blocks as written, they're not quite production level, they're not quite production quality code. And the reason may not be immediately obvious, but imagine what would happen if the user chooses to abort this button functions evaluation, if they choose what is it? Evaluation abort evaluation while that evaluation is so command.if I click the button and then abort that evaluation. So this evaluation is aborted, which means it never gets through to the running false state. So even though the evaluation was successfully aborted, the front end continues to show the indeterminate progress indicator. There's nothing being evaluated here other than the progress indicator spinning. So how do we fix this? Well, we fixed this by using the abort handling features of the Wolfram language. We have check abort. I suppose one could also use with settings for this. But check abort and then abort protect during the cleanup for the abort will effectively so here if we do not abort, it still just takes a couple seconds and then finishes. And if we do abort instantly, that's noticed the cleanup stuff is run and the result is that you get is correct. So some examples of how to show progress during these long running evaluations. I'll also mention that event handler supports the queued method. So that means that buttons in graphics which happen to use event handler can also support kicking off queued evaluations. Here we have an example where on click we go into a running state and then change the color of the disk. So in the running state shows little percolating animator. Some of these colors are more beautiful than others, but so you get the idea there. Okay, so even though button and event handler and action menu, even though those are the only controls that support method queued, that doesn't mean that the other controls can't kick off a long running evaluation. It just means you need to be a little bit more clever in how you do it. So the example that I like to show involves dynamic wrapper. Dynamic wrapper is a function that displays as its first argument, but dynamically updates as its second argument. So it displays as its first argument dynamically updates as its second argument. Second argument is not shown. So this, this is a bit in. In violation of the rule that I said earlier. The dynamic wants to be seen. Dynamic wrapper allows you to to avoid that rule to have an expression that's not seen but is still dynamically tracked. So here we have a checkbox and when that checkbox becomes true, then this kicks off a longish evaluation. And dynamic wrapper takes all the same options as dynamic. So we can say that that evaluation, that dynamic update should happen asynchronously. Synchronous updating false. And it should only care when the value of the checkbox variable changes. So here we have an example of a checkbox that can kick off a long Running evaluation during which you can show arbitrary progress indication and so on. So I think that's a rather nice approach. And of course this works not just with Checkbox, but with, with, with any sorts of variable changes. Okay, now let's dig into one of the Otter constructs in the dynamic family. So I'm going to talk about Control Active and I'm going to motivate it by looking at this example. So we have a, a plot 3D. It's rather complicated and if I drag the slider around, you'll notice that Manipulate has done something rather odd while dragging this. It's not showing the beautiful rendering of this particular plot, it's showing sort of a fast draft rendering of it. And it does this so that the slider can be responsive. Right. We want fast things, we want only fast operations in the up in this, in this realm. Not sure what happened there, but. Ok, so how is this, how was this done? Well, from the plot 3D perspective there is. I'm going to talk about this top down, but then I'll. I'll give the bottom up answer in just a minute. So option plot 3D has an option that specifies whether to aim for quality or for speed. And the value of dollar performance goal changes while a control is being used. So notice what, while it's just sitting here not being used, we're in the quality mode. If I'm dragging around dollar performance goal has changed to speed and then when I release this it goes back to quality. The reason this works in Manipulate, if you look at the output cell bracket by the way, when I release the mouse, you'll see a slow. A flash as if it's briefly evaluating. The reason for this and the reason this works in Manipulate is that the default option for synchronous updating and manipulate is not true or false, it's automatic. And in Manipulate that means that while a control is being used, it's going to be providing synchronous updates, fast updates and then when the control is released it will provide one, you know, slow running update to. On the asynchronous on the Q link. So you know, one can avoid this for plot 3D on very fast. So I'm working on a, not my machine today. This is a very fast machine that can probably handle this pretty well. So that's, that's not quite as, as smooth on my, on my five year old laptop. But so you can, you can override the built in this, this built in behavior by, by setting Performance goal. But Performance goal is actually built up on top of this Control Active primitive. So Control active, you know, as I mentioned, synchronous updating is set to automatic. When that happens, Control Active can switch before it's wrapper that, you know, contains two expressions. The first expression is the fast draft rendering. Second expression is the beautiful, you know, possibly long running rendering. So here we have a, a dynamic that while it's in use, while this slider is in use, the value of X is changing and we're seeing sort of the raw value of X and when we release that, it switches over to the slow running, slower running, in this case artificially slower running expression that involves pause and panel. Right. So this is what's happening. The control active is what's happening, you know, by default for this example. And I just wanted to point out this is, you know, people have actually asked about this, I think this is a thread over a decade old now that somebody was asking for a particular way to do some kind of updates and manipulate the answer for which was, well, if you have a control Active, you can say the fast draft state should look like this. And then the slow, the, the slower, the more beautiful state can do arbitrary things. So in this case it's a dynamic module with an asynchronous initialization that incrementally adds to the graphic as time passes. So as we interact with this, we see the draft rendering is just a simple circle. As I release the mouse, it goes off and runs through, you know, some set of steps that, that adds to the, as I said, adds to the graphic incrementally. So some nice features built on, on Control Active. Right. I mentioned already that, that preemptive evaluations cannot themselves be preempted. That and much more can be learned by studying Todd Gailey's essay linked here on Stack Exchange. There's a lot to be said about so called preemptive evaluation and preempt protect. And it's somewhat beyond the scope of what I wanted to cover here. So I will just leave you with this pointer for people who want to dig in more about what's really happening during these so called preemptive evaluations. And again, don't be afraid to use the slow lane if you're, if you know that evaluation is going to take a while, I'd encourage you not to increase the global setting for the, you know, the, the limit, the dynamic evaluation timeout limit. Just learn to use synchronous updating, false, Synchronous initialization, false and so on. You won't regret it. Right. So that's basically what I wanted to say. I will point you at lots more interface examples. There is, you know, the Wolfram function repository has a, has a section dedicated to user interfaces that users have or user interface like things that users have have submitted. And those, you know, vary from sort of simple just variations on controls so that, you know, here we have a checkbox that looks more like you might see on your smartphone. Somebody has put together a rather nice progress indicator that sort of does beautiful rendering during the evaluation, people have put together complete interfaces including, you know, rebuilding Minesweeper or building ear training utilities and such like. So I'd encourage you to take a look at the. I guess I can. Even so there's, there's lots of user interfacey things that one can find on the Wolfram function repository. And you know, as, as people invent more, we're adding more all the time. So, so come back and, and see that from time to time. All right, so those are the principles that I wanted to cover today. And we're at the end of my prepared remarks. Maybe now I will go to the questions and see if there's things that, that people still want to talk some more about. Question can you use dynamic modules to compute variables within Wolfram to use Wolfram's internal engine software? I'm not quite sure what that question is asking somebody wanted to clarify that. I think this is why I've run into issues displaying dynamic that's probably synchronous things. Question how can I use Trace to tune which strategy I should use to ensure dynamic updates are fluid relative to processing power of the host? So one thing that I find is very useful rather than Trace is to keep a close eye on. And this is not something I've done on this machine. So let me see if this just works. And just out of an overabundance of caution, I will turn off the toolbar and I will also turn off the predictive interface in this notebook. So. Oh, that's not my machine. The geography of this keyboard is just different enough that it's annoying. So if one. So there's a. There is a built in kernel called Link Snooper. What Link Snooper is, is Java program that sits between the front end and kernel and keeps an eye on all of the traffic that the front end sends to the kernel and that the kernel sends back to the front end. So here we see in the case of manipulate. Let me do that one more time. This is the set of commands that are being sent back and forth. So it starts with front End saying, hey Colonel, can you, can you evaluate this for me? And the kernel says, okay, well I'm doing that now. During that evaluation, I need to know what this is and I need to know what you know. And then when it's done creating the output, it sends the boxes back front and says, okay, thank you very much. Now there were some dynamics in there that I need to send back to you. And so there's this communication back and forth. There's this conversation between the front end and kernel. Oops. So keeping an eye on this Link Snooper is important if you want to know in sort of nitty gritty detail what's happening. But even if you don't look at the actual content of each line, you can get a sense of how much communication is, is happening just by looking at the sheer number of lines. And maybe there's some communication that's happening because, you know, you had some dynamic that you had forgotten about or that's unimportant for a particular update. And at that point you would go back to taking a look at, you know, maybe you need to nest your dynamics or get rid of some dynamics in order to simplify updates that happen in a particular interface. So I find Link Snooper rather than Trace. Link Snooper is a, is I think a more useful tool for determining what's going on in interfaces. Okay. General guidelines for updates Dynamic updates via Cloud based Wolfram Document. Cloud support for Dynamics is fairly strong. Cloud does not yet support the full set of controls. I, in order to get the actual listing, I think each page, each. If you look up any of these on, in the documentation, it should have an annotation about whether it's supported on cloud or not. I think the default assumption is that it's supported unless there's an annotation that I believe happens right up here says unsupported on cloud. If that's the case, okay, you were making the frame option using dynamic mentioned it had to do with the front end somehow. How do we know which options can be wrapped up with the dynamic? That's an interesting question. So I think I did that here, right. I the, the point was to drive this dynamic into the lowest possible location. And the, the answer is we don't document this too well. So I think there's, there's no better answer than then, well, you can start building up an intuition about these, but I would suggest pushing it down as far as you can to start and if that doesn't work, then pull it back up. So for instance, in Grid where you might want the first argument of grid to support dynamic. Unfortunately it doesn't. And so if you want a grid that has dynamically changing number of rows or number of columns, you might have to resort to a using dynamic around the grid as a whole. It was pointed out to me that locator is an example of. Yes. So here is an annotation going back to the previous question for just a moment of what it looks like when something is not supported on a cloud and you use dynamic to implement drag and drop cells in a notebook via ui. I think our story for drag and drop is not yet fully been told. We have had some experiments about it in the past and and I think I'm going to resist the urge to say much more than that. Is there a way to ask dynamic module not to remember the value between sessions? For instance, when I want to palette with some internal state to reset? Yes, there is. And the way that one would do that, there is an option to dynamic module that lets one do that and the option is unsaved variables. So it's rule delayed set to a list of of symbol names of, you know. Yeah, symbol names. So you can have this be completely unset when opening. I mean so that's one way to do it. Another way to do it would be to set the value of your variables in the dynamic modules initialization because the initial or to reset them in the initialization because the initialization is another example of an expression that's run automatically when that instance of the of the dynamic module is displayed in a new front end session. So you can manage resetting in in the initialization option as well if that's more convenient or makes more sense for your application. Let's see, where was I? What is the difference between doing this and this? So question is, what is the difference between doing needs? Dynamic module versus dynamic module initialization needs. So the difference. One difference that jumps out at me is what happens if you evaluate these and save the notebook and open it in a new session. When you open that notebook in a new session, this dynamic module output will be displayed without the benefit of having run this needs. So the package that you're loading and this needs will not be automatically run if it's not among the things that this dynamic module automatically initializes. That's not true here. Here you've gone through the effort of saying yes, make sure to load the package while you know before displaying this dynamic module if it's not already loaded. So I think that's a key difference. Good question. Question from Twitch Sometimes I can't find what is firing a dynamic, even though I am using dynamic module. Is there a debugging strategy to find what is causing the firing? Well, there's binary search. There is an internal function that I wonder if I should mention. So there is a. So the kernel generates. When a dynamic needs to be updated, the kernel generates a track event, an event that says so. So. Okay, sorry, let me back up One possible answer to this question. You know how to find out what's triggering is. Is you could look at Link Snooper as I described earlier. You could take a look at Link Snooper and see which you know, if your dynamics are nice and isolated for individual functions, you could mention. You could mention. You could. You could. You could see that in the Link Snooper traffic. But if Link Snooper is not being helpful, the kernel keeps a list of the of the dynamic tracking events that it's sending. And there is a way to see that list. So that system is not exposed by default. It is. It can be turned on by setting Collect track event true. And from this point forward in this kernel session, Collect track event with no arguments will return the tracking events that the kernel has noticed since the previous time that you've either turned the system on or since the previous time that you've collected them. So if we slide the slider around a little bit, each of those changes has triggered a. Sorry, I'm trying to resize the window. Each of those changes has triggered a new tracking event in the kernel. And so we've drained, you know, we. This gets that list and then drains the queue. So if I evaluate it again, that list is gone. I click to change, we see a single change event and then one can turn the system back off with false. So this can be a useful way to see which symbol is the thing that's being that. That the kernel thinks is the trigger for that particular dynamic update. Right? Okay, so manipulate variables. Sometimes I like having course control, sometimes I'd like to have coarse and then finer control. Is there a way to switch back and forth in the same manipulate. By course they give an example of, you know, x0 to some huge number. And for finer control they suggest X minus 1:1. Which by the way, one of these is not entirely within the other. That's fine, but the answer is absolutely. So there's a couple things to say about this. One is, unlike table, there's nothing to prevent you from having from actually mentioning the same variable in multiple control settings in a manipulate. So you can have multiple Controls that both change the value of the same variable. So here we have a slider that goes from minus one to one. Here we have a slider that goes from zero off to however many billion that is. There's no particular reason you can't do that. This is reddening because the value of X is outside the domain of this slider. But it's certainly within the domain of this slider. Likewise, if I slid this to negative one, so. So that's one answer. The other answer is that when slide went. So I guess there's a couple other answers. One is you can up the the manipulator controls and set values directly. If you're interested in what this looks like at a particular value, you can do that or something that I think still works. So I'm now holding down the option key and dragging, which would be I guess the alt key on Windows and dragging. And notice how the drag is not quite staying in sync with the location of the mouse because the drag is happening somewhat slower. Because I have a modifier key down. If I hold down two modifier keys in this case option and let's see, Option and control. No, not control, option and command. I believe the more modifier keys one holds down. Right. So there's option and shift and now it's moving much more slowly with response to mouse movements. So this is something we implemented I guess nearly 20 years ago and maybe most people have forgotten, but. So that's unmodified single modifier and then double modifier for slow dragging. Very useful. I wish there was a simpler way to create controls that are more involved. I know I can do something like dynamic control opener, et cetera. It would be nice if there were a set of functions that did this. Opener, manipulate, control. Yeah, we actually. We actually considered doing creating a separate set of controls in Manipulate to have a Manipulate opener view or Manipulate tab view and decided against it at the time just because of language clutter. I wonder if we should reconsider that. I don't. I personally like having the sort of minimal language, but I can definitely see people appreciating some convenience functions there. I think the spinning beach ball often arises when I'm in an error which makes dynamic output too graphics intensive. Is there a way to preemptively turn off dynamic updates at the front end level? Preemptively turn off dynamic updates at the front end level? I'm not sure what exactly that means. There is a dynamic updating option that can be set. Certainly can be set at the front end level, you can set that to false. This will bust lots and lots of things. Maybe that's what's being asked. And you can set that for a particular notebook. Of course, as you probably know by by interacting with that option in the evaluation menu that affects notebook scope. If you meant something else, feel free to clarify. Every part of this area is functional, looking slick. Just perfect. Thank you. That's nice to hear. Will graphics render faster and high quality with a compiled function? That's somewhat outside of my scope, but I would assume so. I don't know how auto compilation interacts with the performance goal system because performance goal affects so I I mentioned it as if it were a single option in plot 3D, but really performance goal is is looked at by other options. So for instance, I think the mesh option setting for plot 3D and possibly things like plot points maybe. I think there are a few things that the that performance goal actually affects. So it's so. So your question would be better answered by someone who knew more about our visualization functions, and I believe that there are such people in line to be giving these kinds of talks. So. So yes, let's see. Comments. Same for interval slider. I'm not sure what that was referring to, but okay. Oh, and someone pointed out that the last bullet point on slider. Ah, so this is what I was describing moments ago about using modifier holding on modifier keys when sliding a slider. It turns out we did document it, so good for us. Okay, I guess so. 1. One suggestion from John Fultz, who's listening in as far as how to take care of a An evaluation. A dynamic evaluation that has taken over your front end is to simply kill the kernel, and that might be enough to get your your control back. Obviously it's not something we want to try as a first attempt, but if you're out of other ideas, you can use, you know, your process manager to kill the kernel, and maybe that will give you a sliver of time in which you can interact with the front end to do what you need to do. Okay, that I think exhausts the questions. So thank you so much for your attention and it's been fun chatting with y' all about principles for dynamic interfaces, and I look forward to seeing you all in the future. Thanks very much.
More from Guy

Cheryl Strauss Einhorn | The Human Edge: Smarter Decisions in the Age of AI | Talks at Google

Rachel Glennerster | Innovation and the Center for Global Development | Talks at Google

Rebecca Winthrop | Rethinking the Purpose of Education in the Age of AI | Talks at Google

Dr. Robert Wachter | A Giant Leap: How AI Is Transforming Healthcare... | Talks at Google






