Where a Notional Machine Doesn’t Help: JavaScript and the DOM

September 9, 2019 at 7:55 am 14 comments

At the Dagstuhl Seminar on Notional Machines and Programming Language Semantics, I came to a new understanding about where notional machines are not useful and another kind of support is needed.

I joined a breakout group on “Notional Machines for everything else.” There had been discussions about notional machines for popular programming languages for Scratch and Python, and one breakout group was formed around that goal. This group was about exploring where else notional machines could be useful, like trying to understand machine learning, generating proofs, and JavaScript for Web development. Joe Politz was our group leader.

After the first round of discussion, we all decided to focus on JavaScript. We had some serious experts on JavaScript and the DOM in our group, like Shriram Krishnamurthi and Titus Barik. The discussion was amazing! I was learning so much, and I took pages and pages of notes. Everyone noticed my feverish note-taking, so I got elected to report back to the group. That’s this blog post — it’s the work of the whole group (not just me). I just happened to be the guy who made the slides.

We had already come to the realization that there isn’t just one notional machine per language. A notional machine is about helping students understand a computational process, and there can be lots of processes in a given language. So we picked a specific scenario that we were aiming to explain: You (as a student) want to turn the background yellow when the button is clicked.

But the student makes a mistake.

And I came to realize that, without a lot of support:

The problem is that window.bg = "yellow" isn’t wrong. Because there isn’t a previously defined bg property, this assignment simply creates a property bg for the window object. No error. It just doesn’t do what the students wanted. How does the student figure out that the desired property is backgroundColor? Get a list of all bindings on window? There are hundreds or even thousands of them. How do you find backgroundColor among all of those?

The breakout group started listing on the blackboard the things we might need to explain to students to help them understand when went wrong or what might go wrong with clicking on a button to turn the background yellow. It was a long list.

You probably can’t read all of those, so I’ll list a few of them here:

  • What happens if you have two DOM elements with the same ID?
  • Where did window as an object get defined at all?
  • If you have event handlers defined on an object, and you delete the object, what happens to the event handler?
  • What happens if objects higher up in the DOM modify the event triggered by the event click?
  • Something I heard students ask: If my JavaScript changed the DOM, how come my HTML file didn’t change?
  • And Many More

I really had no idea just how complicated JavaScript and the DOM were! Amy Ko looked up the JavaScript definition of what == means (see link here). This isn’t the formal semantics. This is meant to be understandable. It’s insanely complicated.

At this point, Ben Shapiro raised a really interesting side question: What’s the cost of JavaScript’s overly-complicated rules? Is there a way of measuring the lost productivity of bad programming language design?

So, what’s the answer?

I realized that the answer is not a notional machine. The problem is that long list Amy found for us. I can teach part of that list as a notional machine, but I can’t teach all of it with a simplified model. Any simplification I create would be insufficient for the complexity of the reality. And even having a notional machine wouldn’t help if a student typed window.bg = "yellow". The student needs IDE’s and other supports to figure out errors that never trigger an error.

The solution is to reduce complexity to make it teachable.  In an earlier talk at Dagstuhl, Ben Shapiro explained how Shriram and Joe and their collaborators did this with Pyret.  In Pyret, they explicitly disallow some things that Python allows but are way too complicated to explain.

Probably the best idea for JavaScript, following Racket’s lead, is to have language levels. We should teach students a strict subset of JavaScript, where the really complicated things are simply disallowed. The goal is to help students to learn a real subset, then grow the subset.  TypeScript offers an alternative model, because it offers a more sane way of doing JavaScript.  For example, TypeScript’s type checking might help figure out the window.bg bug. There are lots of other languages that are more reasonable and compile to JavaScript — but those are avoiding JavaScript entirely.

The very best idea would be to fix JavaScript and the DOM, but it’s probably too late for that.

This working group was useful to me for two reasons. First, I really do have to teach JavaScript and the DOM (again) in January 2020, and now I have a new sense of the challenges and my options. Second, this was a great example of where a notional machine is not the answer to a pedagogical problem.

Thanks to members of the group who reviewed an earlier draft of this summary.  They’re not responsible for where I still didn’t get the details right.

 

Entry filed under: Uncategorized. Tags: , , .

How the Cheesecake Factory is like Healthcare and CS Education Come talk about the Role of Authentic STEM Learning Experiences in Developing Interest and Competencies for Technology and Computing #STEMforCompTech

14 Comments Add your own

  • 1. Raul Miller  |  September 9, 2019 at 12:34 pm

    As a variation on this theme, consider what a student who has heard of background processing might have meant if they said:

    window.bg= true;

    Anyways, it seems like a significant part of any attempts to untangle such issues should involve the student looking up relevant documentation. And, of course, the other side of this is that people need to be writing relevant documentation…

    Meanwhile, I am not adequately convinced that throwing errors (the dark side of simplification) into the language helps with this example. The student already knows that something is wrong, because the machine did not do what the student intended. Unless the student gets an error message which leads to relevant documentation (which, granted, it might – if people have been writing that documentation and if the error message is unique enough, and if someone thought to link the error message with the documentation, and if people providing speculative answers and/or working on other issues haven’t obscured the connection too much…)

    But, also, it’s not just javascript. Consider, for example the complexities inherent in addition in most any computer language implementation:

    Addition works with different machine types (aka: different concepts of what the underlying bits represent). In some contexts, basic properties of addition are lost (commutativity, associativity, … ). And then there’s multiplication… Is productivity lost because of these complexities? What do we mean by productivity?

    Cutting this short before I go too far off topic…

    Reply
  • 2. alanone1  |  September 9, 2019 at 2:33 pm

    Hi Mark

    Remember Ted Kaehler’s “method finder” in Smalltalk?
    https://wiki.squeak.org/squeak/1916

    It was done to first answer the question “if you don’t know what the sine function is called, how can you find out what it is?”.

    In the method finder you type:

    30 . 0.5

    and the result is degreeSin:.

    or you could try:

    3 . 4 . 7

    and you will get: +, or, and a couple of surprises.

    Let’s say you want to find out what method returns the last element of a collection. Simply enter an example collection, a period, and then the expected answer (which would be the last item), like this:

    #(‘one’ ‘two’ ‘three’). ‘three’

    The Method Finder shows these results:

    #(‘one’ ‘two’ ‘three’) last –> ‘three’
    #(‘one’ ‘two’ ‘three’) median –> ‘three’
    #(‘one’ ‘two’ ‘three’) third –> ‘three’

    Both JS and especially the DOM are a lot messier and ad hoc than Smalltalk, but this approach could still be tried (JS could help more if it were more meta and reflective).

    Reply
    • 3. Michael Ball  |  September 10, 2019 at 3:12 am

      It’s certainly nowhere near as advanced as the method finder, but the web inspector of “Developer Tools” in any browser go a long way towards helping learn the DOM.

      Aside from auto-complete, the really useful thing is that you can log/print an object and just interactively explore the entire hierarchy. There’s also a few common methods (like dir) that can aid in exploration. Definitely not as intuitive as it could be, but a powerful tool nonetheless.

      Reply
      • 4. alanone1  |  September 10, 2019 at 4:08 am

        Well, the method finder is more than 40 years old now, and the dynamic inspectors and explorers in Smalltalk (and before that, Lisp) are much older than this.

        I guess that one part of engineering -training- could be devoted to the current situation of the browser, but this is not even close to what the aims of -education- should be in any decent college. For example, one of the goals of education is to provide vantage points and perspectives that will help the students evaluate and be critical of the status quo. Allowing really bad designs to enter as single examples of something seems like anti-education to me.

        I’m not at all suggesting that Smalltalk or Lisp should be used here — nor Racket, etc. — but that university departments that are supposed to be conducting -real education- should try to understand the use of perspectives, actual best practices, etc. and the differences between understanding and vocationalism when setting up a curriculum especially in something that is actually labeled “Computer Science Education”.

        Reply
    • 5. Mark Guzdial  |  September 10, 2019 at 9:06 am

      Hi Alan,

      I’m concerned that the cognitive load for the method finder is too great. That 30 . 0.5 gives you degreeSin: presupposes that you have some idea what you’re looking for. If you don’t understand the space, the Method Finder is an exhaustive search. The closest to the Method Finder in today’s Pharo is the “Spotter.” People don’t use it well — see this study: http://bergel.eu/MyPapers/Kube15a-VISSOFTNIER-SpotterAnalyzer.pdf. They don’t dig deep. They don’t try to exhaustively search. While I agree that it could work, I think it would only work for econs, not humans (in Thayer terms).

      Mark

      Reply
      • 6. gasstationwithoutpumps  |  September 10, 2019 at 11:18 am

        I agree that method finders are not very useful—you have to have some idea what you are looking for and be able to recognize the method name when you see it. Most method finders would be incapable of finding sin(d*pi/180) as the method for 30:0.5, though that is the correct method in many languages for “degreeSin”. A student with no clue is more likely to choose a simpler method, like d/60, unless they have thought about trigonometry and realized what function is needed.

        Documentation with good indexing (or good search capabilities) is far more useful than method finders. Consistent naming and syntax across different types (so that addition is always ‘+’ for example) helps a lot.

        Reply
        • 7. alanone1  |  September 10, 2019 at 11:33 am

          I can see your misunderstanding.

          What is found is the actual operator/function name — degreeSin: — that is used by Smalltalk to produce the result. It’s not a description, but what you need to know. The “method” that is “found” is the actual Smalltalk method to accomplish the relationship.

          This was done by Ted in Smalltalk by safely executing every method in the live language — took about 1/4 second 40 years ago — to find all the matches. It’s not a data base search, but a semantic match.

          Reply
          • 8. gasstationwithoutpumps  |  September 10, 2019 at 6:56 pm

            I understood your example—but the programmer has to know that degreeSin is the right method, and d/60 and cos(d)/sqrt(3) are not. Pattern matching on I/O behavior is not sufficient.

            It is also essential for the desired method to be in the search space, which often will not be the case—degreeSin happened to be for Smalltalk, but in many systems the same function would have to be constructed from other primitives. Once you open up the search space, it gets too large to search effectively. For example, if you don’t have a “first” operator, but only subscripting, constructing first(x) as x[0] is trivial, but searching all of the possible x[n] functions because one of them might be the desired function increases the search space enormously.

            Reply
  • 9. Michael Ball  |  September 9, 2019 at 4:39 pm

    I really like this post! However, the one thing that I might quibble with is “JavaScript and the DOM” conflates a couple things.

    First, it technically conflates the idea that the DOM has to be manipulated by JS. The DOM is language and platform independent. Now, I will grant you that in reality there are very few examples of truly non-JS based DOM manipulation. (Most of the non-JS things are really just JS compilers.)

    But it’s worth mentioning because if we’re talking about a notional machine, it’s not necessarily “JavaScript” that we need to deal with. It’s, IMO, more of a browser.

    Second, JavaScript does exist entirely independently of the DOM. And this is quite common and prevalent. There’s many different non-browser environments which you can use JS and still experience the language quirks, but in this way it’s much closer to a Python or Ruby environment.

    That’s not to say I disagree with the idea of subset, but I think one idea would be to change the context. Or to teach things in steps. Learning web development is learning a million things at once (HTML, CSS, DOM, JS, special tools) and I think there’s a way to disentangle these. However, the web as a vehicle for learning is incredibly engaging so I don’t think I would want to forgo that, but it would change how I approach teaching JS.

    It is also worth mentioning, that unlike Python or Ruby and perhaps more(ish) like Logo, there is no one canonical JS interpreter. Each browser/OS/server environment has its own quirks. They are generally compatible, but if I were teaching a course, I’d need to think hard about how to minimize that.

    Reply
    • 10. Mark Guzdial  |  September 9, 2019 at 8:59 pm

      Both of these observations are certainly true, but the reality is that JavaScript most often appears in CS curricula in Web development classes. So while the DOM could be taught separately, and JavaScript could be taught separately, the need in the curriculum is typically to teach them together.

      These points were made explicitly by more well-informed colleagues at Dagstuhl. That I left those points out of the blog post is my fault and not theirs.

      Reply
      • 11. Michael Ball  |  September 10, 2019 at 2:56 am

        You certainly need both in a web dev course, but what I was trying to suggest is to separate when something is a DOM (or even a browser API) and when something is plain JavaScript and how you can figure that out.

        I haven’t yet taught large front-end web courses (on my list!), but a full stack course, as well as workshops, 1-1s, etc and I guess the place where it mostly helps is letting students distinguish errors and where to look. Depending on the projects students do, or even the frameworks chosen (especially React, etc.) students will get more experience with JS language features than the DOM and I’ve quite liked that. However, that’s a trade-off I can certainly see why you might not want to (or be able to…) take.

        I’m not sure it’d help much with a notional machine, but the more explicit I am with students about which piece is which, the more they’re able to isolate something confusing to a particular technology. At least, that’s the goal when things go well.

        Otherwise, reading this reminded me of an additional tool: One way to reduce the scope of JavaScript is to provide some linter rules to students, like a custom ESLint config. It operates pretty similarly to Java’s check style and while annoying (for you/TAs) to configure, that’s something you only need to do once. And despite the name, it’s much closer to a static analysis tool than a linter — lots of room to define custom rules.
        It has the same challenges that TypeScript, etc do in that it’s yet another dependency to bother with.

        And, finally, while I would never recommend this for a production web application, I would consider leaning into recent JavaScript features that only work on modern browsers, and skipping all the pain of tooling that allows you to compile that code for older machines. I don’t know if anyone has yet done formal work on whether some of the new features are easier to learn, but anecdotally I’ve had much better experiences with features like the new class syntax, and I often won’t even teach the var keyword until I get asked…it’s all let and const.

        Reply
        • 12. Mark Guzdial  |  September 10, 2019 at 9:04 am

          My problem with that approach is that you force students to ignore the status quo. Most of the code that’s on the web uses “var.” How can you avoid teaching it, then? Our students don’t live and learn in the bubble of our class. One of my favorite Alan quotes: “You can fix a clock. You have to negotiate with a system.” Web development is an ecosystem.

          Reply
        • 13. Evan Cole  |  January 6, 2020 at 4:30 pm

          A bit late to the game, just found this article/discussion and love it. Nice to find others talking about JS notional machines!

          With JS & web dev I think you do have to take a different approach than simplification or language levels. It’s not possible to simplify like you can with tailor-made or less integrated languages, both because of existing status quos and inherent complexity.

          By the time you’ve simplified things enough, you might as well be using a true beginner language since you’ll have to remove so much of the “realness” and interactivity that makes browsers an engaging platform. I think the goal should be to find ways of making the whole system of technologies more approachable, pre-digesting the complexity instead of trying to remove it.

          What seems to work well is to lean into the practical “hack it” aspect of web development with fun projects to motivate and contextualize, but then pair the projects with focused exercises that explore how one or another of the involved technologies work. In these exercises you can introduce notional machines for different aspects of the browser environment, and have some degree of language levelling control over what aspects of JS students study in depth.

          What Michael said about conflation is also dead on when it comes to curating these side exercises & notional machines. Even “within” javascript there are multiple levels of notional machine that can be detangled relatively easily using existing visualization tools (been using this approach in classes with qualitative success: https://awesome.hackyourfuture.be/notional-machines/javascript). While this isn’t a formal method of categorization, it is practical and keeps students moving forward. You can’t stop students from coming across things like “var” but you can prepare them to deal with it. It’s more realistic to accept as a learning objective that students can detangle language features when they come across them rather than expecting them to understand it all outright. And more than adequate in this case.

          One thing I find very empowering about web development (that I’m not aware of being true with other languages) is the short distance between the code students are learning to write and the sites/tools they can use to study. By writing simple evaluation & feedback tools with JS/html/css you can turn the browser itself into a rich study environment using code a student could understand after a few months study. This “flat” learning environment really appeals to me. Using compile-to-js languages (coffeescript, typescript) may simplify the code students need to write for some tasks, but also puts another layer between the student and the interpreter. Linter rules are a neat idea, that’s next on my list to test out 🙂

          Reply
  • 14. Author  |  September 9, 2019 at 10:10 pm

    Little League teaches baseball like this. The littlest kids play tee ball where they hit the ball off a tee, swing loads of times, and never get out. As they get more skilled, they introduce coach-pitch and eventually pitchers; limited tries and various outs. Otherwise it would be too much for the kids to learn all at once.

    Reply

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Trackback this post  |  Subscribe to the comments via RSS Feed


Enter your email address to follow this blog and receive notifications of new posts by email.

Join 11.4K other subscribers

Feeds

Recent Posts

Blog Stats

  • 2,096,642 hits
September 2019
M T W T F S S
 1
2345678
9101112131415
16171819202122
23242526272829
30  

CS Teaching Tips