Friday, July 22, 2022

Tips for planting milkweeds in the Bay area

Since the recent article about the endangerment of Monarch butterflies, a lot of people are interested in planting milkweeds for Monarch caterpillars. I've been doing that for the last few years in my garden in the east bay in California, so I thought I'd share my personal tips.

Milkweed species: Narrow-Leaf

There are many species of milkweed, but not all of them are native to California, and non-native milkweeds are associated with issues like disease and disrupting natural butterfly cycles. The two most commonly sold native milkweeds in CA are the Narrow-leaf milkweed and the Showy milkweed. I have planted both, and based on my observation of caterpillar behavior, I highly recommend the Narrow-leaf. My caterpillars will only eat the Showy as an absolute last resort, and sometimes not even then. They are ravenous for Narrow-leaf, however.



Where to buy

You can use Calscape.org to find local nurseries that sell narrow-leaf milkweed. Before you go to the nursery in person, check their website or give them a call to see if it's currently in stock. 

You can often get Narrow-leaf from a generic nursery that sells both native and non-native plants, but some of the generic nurseries spray their plants with insecticide or use soil with insecticides - no good! If you instead go to native nurseries, that shouldn't be an issue (but you should double check just in case). My favorite native nurseries are Oaktown Native and Watershed.

You can also grow it from seed fairly easily. My favorite native seed source is Larner Seeds. It will take some time for the plant to grow large, and small plants may get overwhelmed early by caterpillars, but I've found that even the small plants can bounce back after being devoured. 

Planting wildflowers

There are two reasons to plant native wildflowers near the milkweed: 1) munchies for the adult butterflies 2) familiar places for the caterpillars to pupate. We happened to plant a Salvia Clevelandii near our milkweed, and that's where a caterpillar happily pupated. 

Once again, I recommend purchasing native wildflowers either from local native-specializing nurseries or from seed. Use Calscape.org to make sure that a particular plant is actually native to your area.

Moving the caterpillars

I will often find that a caterpillar will have completely decimated milkweed in one part of my garden (since my milkweed are still quite small). In that case, I often move the caterpillar to a more milkweed-y part of the garden. To safely transport, I make sure that they're actively moving (i.e. not in a delicate phase of changing instars), snip off the milkweed segment with scissors, and place that segment near the new milkweed. Sometimes I even bring them to the neighbors' milkweed if we're all out.

Where do they pupate?

This is still my top question as a Monarch-raiser, as I love to watch the metamorphosis but can rarely find a chrysalis. In my garden, the only chrysalis I located was on our Salvia. 

Here's the butterfly that emerged from the chrysalis on the Salvia (and video of their  first flight):

For my neighbor's garden, they love to pupate on the underside of the top of their fence. 

It's important that wherever they pupate, they have enough room for their wings to unfold and dry out. I'm curious to hear where other Monarchs pupate; let me know what you've seen!

Wednesday, July 20, 2022

Line highlighting extension for Code Mirror 6

A little background: Dis This is my online tool for viewing the disassembled bytecode of Python snippets. I started it off with a simple text editor, but I wanted to upgrade it to a nice code editor with line numbers and syntax highlighting.

The most commonly used online code editor libraries are Monaco, CodeMirror, and ACE. I believe Monaco is the most full featured and accessible, but I opted to try CodeMirror for this project as I don’t need as many features. (I avoided ACE since its what we used for the Khan Academy coding environment, and we found it fairly buggy).

CodeMirror recently released a new version, v6, and its quite different architecturally from previous versions.

One of those differences is that the library can only be loaded as a module and cannot be loaded via CDN, so my first task was adding module bundling via rollup.

Once I got rollup running, it was fairly straightforward to get a basic editor working:

import {basicSetup} from 'codemirror';
import {EditorState} from '@codemirror/state';
import {python} from '@codemirror/lang-python';
import {EditorView} from '@codemirror/view';

const editorView = new EditorView({
    state: EditorState.create({
        doc: code,
        extensions: [basicSetup, python()],
    }),
    parent: document.getElementById(“editor”),
});

But now I wanted a new feature: bi-directional line highlighting. Whenever a user highlighted a line in the editor, it should highlight relevant rows in the bytecode table, and vice versa. The end goal:

To try to understand CodeMirror's new approach to extensibility, I did a lot of reading in the docs: Migration Guide, System Guide, Decorations, Zebra Stripes, etc. Here's the code I came up with.

First I make a Decoration of the line variety:

const lineHighlightMark = Decoration.line({
  attributes: {style: 'background-color: yellow'}
});

Then I define a StateEffect:

const addLineHighlight = StateEffect.define();

Tying those together, I define a StateField. When the field receives an addLineHighlight effect, it clears existing decorations and adds the line decoration to the desired line:

const lineHighlightField = StateField.define({
  create() {
    return Decoration.none;
  },
  update(lines, tr) {
    lines = lines.map(tr.changes);
    for (let e of tr.effects) {
      if (e.is(addLineHighlight)) {
        lines = Decoration.none;
        lines = lines.update({add: [lineHighlightMark.range(e.value)]});
      }
    }
    return lines;
  },
  provide: (f) => EditorView.decorations.from(f),
});

To be able to use that effect, I add it to the list of extensions in the original editor constructor:

extensions: [basicSetup, python(), lineHighlightField],

Now I need to setup each direction of line highlighting. To enable highlighting when a user moves their mouse over the code editor, I add an event listener which converts the mouse position to a line number, converts the line number to a “document position”, then dispatches the addLineHighlight effect:

editorView.dom.addEventListener('mousemove', (event) => {
    const lastMove = {
        x: event.clientX,
        y: event.clientY,
        target: event.target,
        time: Date.now(),
    };
    const pos = this.editorView.posAtCoords(lastMove);
    let lineNo = this.editorView.state.doc.lineAt(pos).number;
    const docPosition = this.editorView.state.doc.line(lineNo).from;
    this.editorView.dispatch({effects: addLineHighlight.of(docPosition)});
});

To enable highlighting when the user mouses over rows in the corresponding HTML table, I call a function that converts the line number to a document position and dispatches the effect (same as the last two lines of the previous code).

function highlightLine(lineNo) {
    const docPosition = this.editorView.state.doc.line(lineNo).from;
    this.editorView.dispatch({effects: addLineHighlight.of(docPosition)});
}

For ease of use, I wrap all that code into a HighlightableEditor class:

editor = new HighlightableEditor(codeDiv, code});

Check out the full highlightable-editor.js code on Github.

Wednesday, July 13, 2022

Inactivity timer for Chrome extensions

My Quiz Cards browser extensions are interactive flash cards, giving users a way to practice their Spanish, German, US Capitals, and World Capitals, by simply clicking an icon on their browser.


Screenshot of Quiz Cards popup asking a spanish word

One of the Quiz Cards features is updating the browser icon with a little number to indicate how many days have passed since the last time you answered a card. 

When I upgraded the extension to manifest v3, I found I also needed to update that feature.

How it worked in manifest v2: whenever a user answered a question, the extension stored a timestamp in localStorage. The background script used setInterval to call a function every so often to see how many days passed since that timestamp, and if more than 0, it updated the badge with the number of days.

When using manifest v3, background pages are actually service workers. Using setInterval will no longer work reliably, since the browser stops and starts service workers when not in use. Instead, Chrome recommends using their alarms API instead. They also suggest using their storage API instead of localStorage.

So, in the flash card pop up, I run this code when a user answers a card:

chrome.storage.local.set({'last-asked': (new Date()).getTime())

chrome.action.setBadgeText({text: ''});


That stores the latest timestamp in storage and clears out any number that might have been previously set on the badge.


In the background service worker, I set an alarm to call a function every 60 minutes. That function retrieves the timestamp from storage, compares it to the current time, and updates the badge number if relevant.


async function sync() {

    const result = await chrome.storage.local.get(['last-asked']);

    const lastAsked = result[key];

    if (lastAsked) {

      const rightNow = (new Date()).getTime();

      const timeDiff = (rightNow - lastAsked);

      const DAY_MS = 86400000;

      if (timeDiff > DAY_MS) {

         chrome.action.setBadgeBackgroundColor({color:[0, 0, 0, 255]});

         const numDays = Math.floor(timeDiff/DAY_MS);

         chrome.action.setBadgeText({text: numDays + ''});

      }

   }

}


// Once an hour, check if it's been too long

sync();

chrome.alarms.create('check-inactivity', {periodInMinutes: 60});

chrome.alarms.onAlarm.addListener(sync);


And that's it! I figure this may be a common use case for the alarms API, so I'm hoping this post helps anyone looking to implement a similar feature.


Sunday, July 10, 2022

Diversifying historical references in CS classes

 A few years ago, I discovered #DisruptTexts, a movement by teachers to challenge the traditional canon in English classes, i.e. spending less time on Shakespeare and more time on historically underrepresented authors.

I teach programming, not English lit, but I still wondered if there were opportunities in my curriculum to shift emphasis away from white male “authors” to people from other cultures. I realized that there are a few standard spots in programming courses that reference the inventor of some algorithm or concept, and that inventor is typically a white male. However, as a general rule, many "inventions" actually get invented by multiple people, and history books try to simplify it down to a single origin story. So, each time I found a reference to an inventor in my curriculum, I researched the web to find any additional inventors.


Here’s what I found for the topics I taught last year:


Fibonacci → Virahanka


When teaching iteration or recursion, I often show how to calculate the numeric sequence where each number is the sum of the two numbers before (1,1,2,3,5,8,13,…). 


The sequence is typically named after Fibonacci, an Italian mathematician from the middle ages who encountered it while modeling the reproduction of rabbits. 


Slide on Fibonacci's study of rabbit reproduction


As it turns out, a Sanskrit grammarian named Virahanka discovered the same sequence many centuries before, while modeling the syllabic structure of Sanskrit poetry. 


Slide on Virahanka's study of Sanskrit poetry


I love when programming and linguistics overlap, so i was fascinated to learn about Virahanka's sequence. To acknowledge the earlier discovery, I added slides on Virahanka and renamed the fib functions in CS61A to virfib.

def virfib(n):
  if n == 0:
    return 0
  if n == 1:
    return 1
  else:
    return virfib(n - 1) + virfib(n - 2)



Backus-Naur → Panini-Backus


Backus-Naur Form is a notation for describing the rules of a language’s grammar (as long as it is context-free). IBM engineer Peter Backus invented the notation in the 1950s to describe the grammar of Algol programming language:


Screenshot of section on syntax of for statements from ALGOL report

BNF is still used today to describe the grammar of modern programming languages. For example, the SQLite syntax reference uses railroad diagrams, a visualization of BNF grammar rules:


Screenshot of DELETE reference for SQLite



I was amazed to discover that this recent “invention” also has a much earlier origin story from the study of Sanskrit. Around 500 BCE, a grammarian named Panini used a formal notation to describe Sanskrit, and that system was very similar to what Backus created in the 50s. I won't share the Sanskrit rules since I don't read Sanskrit and can't be sure of what I'm sharing. However, here's a grammar diagram from a Sanskrit teacher:

Grammar diagram for Sanskrit

As you can see, the construction of a word in Sanskrit follows similar flows as the construction of a DELETE statement in SQLite. There are elements that can be repeated, elements that must be at the beginning or the end,  recursive elements, etc. Those are the sorts of requirements that could be described both by Panini's 500 BCE notation or by Backus' 1950s' notation. 

For that reason, some propose renaming BNF to Panini-Backus form. I did not end up doing that when I taught BNF, since we were also teaching EBNF and I wasn’t prepared to also rename that. However, I do mention its much earlier invention in India when lecturing in class.


George *and* Mary Boole


One of the most foundational concepts in programming is logic, the way we can combine true/false expressions using AND/OR/NOT/etc, and yield a true/false result. That’s called Boolean logic, named after the English logician George Boole who described the system in the 1854 book, The Laws of Thought.


George Boole was married to another mathematician, Mary Everest, who also wrote books on math, despite living at a time when women weren’t welcomed in academics. Mary is known to have contributed heavily to the editing of George’s book and was a loud proponent of its ideas after George’s early death. I am certain that many inventions of married men throughout history were helped significantly by their significant others but not attributed to them. How many women and non-binary inventors would we know about today if history wasn't so rife with patriarchal systems?


Fortunately, we know about Mary’s contributions. Heres how I describe it in my co:rise Python course:


Booleans are named after George Boole, a self-taught logician. He described a system of logic in an 1854 book, The Laws of Thought, that was edited by his wife Mary Everest Boole, another self-taught logician. According to Mary, George was inspired by Indian logic systems dating back to 500 BCE. We can actually find the origins of many "modern" computer science concepts in ancient systems of India, Africa, or the Mediterranean.


And look, another reference to Indian scholars from thousands of years ago! It makes me wonder how many times logic was independently invented across the world.


Those are the places I found so far where I could diversify the historical mentions. Unfortunately, most inventors are still male and from the upper class of society, since we history rarely hear from oppressed genders and classes. 


I’d love to know if other CS teachers have discovered ways to #DisruptTexts in your CS classrooms. 

Monday, July 4, 2022

How to audit CS61A

I taught/co-taught CS61A at UC Berkeley for the last three semesters. Since it is a fairly well known class, I often get asked how to audit the class, both by Berkeley students and people outside of Berkeley.

Generally, CS61A materials are accessible online, so you don’t need special permission to audit the class. Here are some tips I often give, however.


If you are happy to follow the pace of the current semester (i.e. summer/winter/fall), then use the materials at cs61a.org. You will need to wait for assignments to be released, and you can see those release dates on the front page calendar. 




Otherwise, if you want to be able to blaze through the materials at your own pace, you can access previous semesters by navigating to <semester><year>.cs61a.org, where <semester> is either “su”, “fa”, or “sp”, and <year> is the last two digits of the year. For example, sp22.cs61a.org is the spring 2022 semester when I solo taught, and fa21.cs61a.org is the fall 2021 semester when I co-taught with John Denero. Each semester differs slightly in terms of content and instructor. Denero is the most common instructor and the one who originally created the Python version of the course (CS61A was originally taught 100% in Scheme).


All the assignments (labs/homeworks/projects) are autograded using a system called OKPy that checks whether your code passes the tests. By default, the OKPy command asks you to login to a Berkeley account for backup/submission purposes, but you can bypass that check by adding `-- local` to the command. That allows you to check all your work locally regardless of whether you're a Berkeley student or not. 


The official solutions for the assignments are only available during the current semester, released ~3 days after assignments are due, but are taken down once the semester is over. So, if you think you'd benefit from seeing the official solutions, you should follow along with the current semester instead of going through a previous semester's materials.


Lecture slides are linked from the front page calendar. The lectures themselves are either over Zoom or in-person, depending on the semester. If you're in the Berkeley area, you actually can stop by the lectures in-person. However, if you're not a Berkeley student, you typically cannot access the lecture recordings, as they are uploaded to services that require a Berkeley account. We have to keep the recordings internal for legal reasons, as the recordings are not properly closed captioned, and any published recordings from a university must be closed captioned. 


However, John Denero has a set of pre-recorded lectures that are closed captioned and available on YouTube. Those lectures are often linked from cs61a.org in some way. For fa21.cs61a.org, follow the "Playlist" link under each lecture title. For sp22.cs61a.org, click the lecture title and watch the embedded player. For summer 2022, click "Precorded" under each lecture title. When Denero is one of the lecturers of the semester, his videos are often fairly well aligned with the official lectures. However, when he's not one of the lecturers, there will be some divergence in the content, and sometimes there will be no Denero lectures available for a particular topic.


The textbook was written by Denero and is available for free online at composingprograms.com. The front page calendar has a column which lists which textbook sections are relevant to that lecture. Once again, the textbook readings will be the most aligned in a Denero-taught semester and may be divergent/missing for some topics in non-Denero semesters. 


Typically, the lectures cover similar content as the textbook, so you could decide to only read the textbook or only watch the lectures, and not really be missing anything. When I went through the materials, I primarily read the textbook and only watched videos when I felt like I wasn't really grasping something and wanted another explanation.


Hope that helps!