Check if a cell is defined

You may want to utilize a MutationObserver for that, instead of polling.

Hey, at least it has linebreaks! :smiley:

I can think of two options. You could either proxy (i.e., wrap) the setters on the relevant Sets / Arrays (assuming the references are kept inside the Runtime; you’ll have to check that yourself). Or you could listen for editor events (try adding and changing a cell in the notebook):

Since you already have access to the Runtime, you might also be able to add an Inspector and check for the name there.

1 Like

Ah, super cool! This is slick, but would only capture changes (so if a person re-loads their notebook, defined cells wouldn’t be checked). I think I’ll try out the above approaches and see where I can get!

So, I think I’ve managed to (mostly) combine these approaches, though I need to run (any) cell to get them to all evaluate. I may be misinterpreting something about how mutables get defined – do you have any sense as to why I need to evaluate the mutable variables cell to get it to return anything (on page load, it’s blank). Thanks so much for your help!

You need an additional import to trigger the scenario that allows capturing the Runtime on load. Reimporting from the current notebook suffices:

import {runtime as dummy} from '12e6887297f406bc'

I suggest that you go over the documentation in my notebook to understand how/why the capture works, keep an eye on the JS console, and also play around with the DEBUG option (while your dev tools are open).

Also, I might not have been clear enough that I really don’t recommend this approach. If you describe your use case / overarching goal in more detail, then perhaps we can find a better solution that doesn’t involve awful hacks like this one. :slight_smile:

1 Like

Thanks so much (again!). If you’re open to providing feedback // ideas, the use case for this is constructing teaching notebooks that check student work. When implemented, it would look something like this:

// Check if the cell named `my_variable` stores a value that matches an answer
checkValue("my_variable")

Here is a notebook that I wrote showing how it works in JavaScript using eval(), but not with Observable cells. If you have the time, I’d love to hear your thoughts!

I’d go for a mix of MutationObserver and dynamic notebook imports. Will try to assemble an example tomorrow.

1 Like

Here’s another approach, using side-effects and a mutable which stores the last non-error value of the cell you want to check, and null otherwise:

This requires two additional cells for each cell you want to observe dynamically, but it should be less brittle than some of the other approaches described above.

4 Likes

Thanks! The alternative of just defining the cells is where I started – it really just requires cell_name = undefined for each prompt, which isn’t terrible (the rest of this approach is mostly ripped off of @anjana). https://observablehq.com/d/3e5dbd7eab223d0a

I was mostly just curious if there was an available workaround, and @mootari and @Fil have provided some great ideas!

Just to clarify, I was suggesting something like this in your appendix:

mutable maybeProtests = []
{
  mutable maybeProtests = protests;
  invalidation.then(() => mutable maybeProtests = []);
}

Then instead of

check_answer({protests}) 

You say

check_answer({protests: maybeProtests})

This technique could be extended to track the other answers, too.

2 Likes

And here’s the technique applied to your notebook:

1 Like

Awesome, thanks for taking the time to put that example together! I feel like I’m circling the same problem, but is there a way to avoid having to repeat the same step for each prompt (e.g., the redundant update("var_name", varname, invalidation)? For example, if I could iterate through Object.keys(answers) and perform the update for each one? Feels like I’ll run into the same issue (which, again, isn’t a major issue – mostly just curious at this point!).

Thanks again!

There’s not currently a way to avoid a separate update(…) cell for each variable; combining the cells would mean that a single error would cause multiple updates to fail. It’s the best I could think of for now. :slight_smile: If we add error handling to Observable JavaScript then there will be better ways to express this.

2 Likes

Some more questions:

  • What type of values are you planning to check? Scalars (number, string, bool …), or also objects like Array, Map, Object …? If the latter, how would you perform these checks (e.g., by reference, or by checking specific key/values)?
  • Is the purpose of the check to guide your students while they work on a notebook, or to verify their work after they’re done? Would it be acceptable if only the link-shared/published version of their notebook could be checked?
1 Like

Totally makes sense – thanks for clarifying!

Thanks for following up!

Variable Types: I was planning on checking numbers, strings, booleans, as well as arrays and possibly objects. I’m not looking for a solution to every possible case, but started working on it here https://observablehq.com/@uw-info474/utilities#check_answer
Purpose: My first/primary use case is for students to get feedback while they work on a notebook, but it would be interesting to also think about applying it to checking the notebook (I’ve done some work with Jest testing for assignments in other web development courses). We’re working in an Observable team, so most notebooks remain private but shared within the team (ideally it would work without publishing it).

Thanks again!

I couldn’t come up with a better solution, and frankly I’d prefer Mike’s solution over something fragile that can break at any moment (and in turn frustrate students).

But perhaps we can take a step back and focus on the problem that you’re trying to solve:

  • Why do you feel that the possibilities within Observable aren’t sufficient?
  • Is there some student feedback that you can share? E.g., are the error messages too aggressive/offputting, or too vague?
  • Is there a specific scenario that we could use as basis for this discussion?

Thanks so much for your time and thoughts! To step back, I’ve realized through multiple quarters of teaching using Observable that data wrangling is a big pain point – in turn, I wanted to develop structured notebooks for guided practice. As a bonus, I wanted students to know if they got a “correct answer”.

In my current solution, my only real pain-point (as an instructor), is having to to define each cell as undefined to provide a “cleaner” (i.e., error free) set of feedback to students as they move through the notebook – see here, where I have to pre-define the cell:

// What is the mean number of attendees? `mean_attendees`
mean_attendees = undefined // this is the line I didn't think I'd have to write

Obviously not a big pain point, but it did surface an issue that I found curious – that I could use eval() for JavaScript variables in way that didn’t work for Observable cells (as described here).

I appreciate better understanding the runtime and approaches you all have described. I think the smoothest way forwards (as an instructor) that gives students the clearest experience in each notebook is to simply define cells as undefined (I find it a bit easier than Mike B’s solution to repeat update("var_name", var_name, invalidation) for each variable.

Again, thanks for your time and thoughts!

You may already be getting tired of this discussion, but I’d still like to follow up with another question. :slight_smile:

In your example above, what are the problems that the presence of this line creates?

  1. Too much guidance/handholding, because students should add the cells by themselves?
  2. Having undefined as initial value may be confusing to JS/programming beginners?
  3. Students may fail to recognize that they are supposed to replace the value?

If it’s 2. or 3., would it help to use a placeholder prompt like the following?

mean_attendees = REPLACE_ME
REPLACE_ME = undefined
2 Likes

Thanks for your reply! I hadn’t tired of the conversation, but signed off for the holiday break. In terms of the “pain point” experienced, I’d say it was more like #1 (students should create the cell and name the variable themselves). But, because I have to tell them the name of the variable, it doesn’t actually create much more work on my end.

FWIW, I really like the idea of using the REPLACE_ME variable! Again, thanks for all of your thoughts, they were helpful and interesting!

… but @edeboursetty did! Check out this thread: