Teacher's Guide: Anonymous Functions
Overview
The primary focus of this introduction to anonymous functions is on the convenience this language feature offers. Reference is made to other benefits, such as avoiding namespace collisions, though more advanced constructs that typically make use of anonymous functions (IIFEs and closures) are put off until a later lesson. Lesson activities provide an opportunity to practice with anonymous functions, largely in the context of anonymously defined event handlers. Lesson activities also increase student familiarity with the jQuery methods each
, plan
, and done
.
More about the lesson
Anonymous functions simplify many coding tasks, particularly those involving callbacks. They can also make code easier to read. They both facilitate writing more concise code and obviate the need to come up with names for functions that are used only one time, such as those involving event handler functions, as these simply examples illustrate:
keydown "space", -> $('body').css backgroundColor: random(color)
$(document).click (e) -> turtle.turnto(e).moveto(e).dot(black, 15)
The convenience of using anonymous functions cannot be overstated, and surely accounts for their extensive use. In fact, students have undoubtably come across anonymous functions already, though they might not have recognized them as such. For example, in the Pencil Code Blocks palette, all of the snippets that involve callbacks make use of anonymous functions, such as these two in the "Control" tab:
each
The jQuery each
method provides a convenient alternative to explicitly iterating over a jQuery collection. From a pedagogical perspective, introducing it as part of this lesson provides a good opportunity to work with callbacks and also anonymous functions. But because each
is such a widely used jQuery feature, familiarity of it is essential for making sense of others' code
plan
The jQuery plan
method was introduced in the notes to the Callbacks! lesson, which also provided several opportunities for exploration in the additional activities section of that document. The basic functionality of plan
is staightforward: it delays execution of a block of code, contained in a callback, until a specific point in the animation. plan
is useful when working with functions such as distance
, getxy
, remove
and css
. Without plan
, these functions would execute immediately, at run time, and not produce the results we want.
Calls to plan
have a similar affect as calls to await done defer()
, in that they enable you to control the timing of the execution of functions that aren't normally syncronized with animations. The benefit to using plan
is that it has a much less dramatic impact on program execution than the alternative. plan
simply inserts code to be run at the right time. Calls to await done defer()
, in contrast, block execution of all subsequent code until exisiting animation queues have been fully carried out.
Given the significant effect of await done defer()
on program flow, it is generally a bad idea to embed this statement in custom functions. Embedding calls to plan
in custom functions, however, is appropriate. In fact, the source code for Pencil Code makes frequent such use of plan
.
Notes to activities
A challenge to working with plan
is that values generated through execution of the callback cannot be passed back to the rest of the program. The reason for this (explored further in the Technicalities section of these notes) is that plan delays the execution of the code in the callback until a future point in time, determined by the animation, while the rest of the code in the program is executed immediately.
As an illustration of this pitfall, consider the following script. When run, it does not report correct coordinates; instead, it reports undefined
for both variables:
x = y = undefined for [1..10] fd 25 plan -> [x,y]=getxy() see x, y
The upshot is that the callback must be, essentially, self-contained. For the purposes of the activity, this means that any code involving conditional logic based on output from getxy
or inside
must also be included in the callback passed to plan
.
The backdrop for the MoveOnWhenDone activity is the recommendation, made in prior events-related lessons, to use speed 0
when working with events in the context of current animations. This activity suggests a useful alternative.
Students may benefit from a refresher on jQuery selectors before diving into the jQueryEach activity. Recall that the Calling all $('.turtle')s! lesson introduced students to jQuery class and element selectors. In contrast to the jQuery id selector, which returns at most a single element, class and element selectors can generate collections of page elements. This jQuery collection has all the features of other jQuery objects, which in Pencil Code means that it can be manipulated much like any other sprite. In order to gain access and give instructions to individual sprites, that lesson instructed students to iterate over those collections using standard for loops. This activity instructs students to use the alternative strategy afforded by each
.
It can be helpful to provide examples highlighting different ways of working with jQuery collections. Solutions to the jQueryEach activity will likely include some code such as this:
$(".turtle").each (i, element) -> $(element).wear random(color)
Contrast this with the following call to wear
, made directly to the collection:
$(".turtle").wear random(color)
This latter statement results in all sprites wearing the same color, in contrast to the differentiation accomplished in the example above by using each
.
Additional activities
- Now might be a good time to do actvities from prior lessons that students did not previously complete. Virtually any activity from lessons that involve callbacks, and additional activities from the notes to those lessons, can be completed using anonymous functions.
Code a program that allows the end-user to create a HandDrawnSprite. Provide the end-user more control by setting up key bindings as well, such as number keys for pen thickness, mouseup/mousedown for pen up/down, and different letter keys for colors (āoā for orange, āvā for violet, etc.). You might even add a button that when clicked allows them to enter a name and save the resulting image as a
png
file for later use, using thesaveimg
method, e.g.,handSprite.saveimg fileName
.- BattleRoyale: Create and extension of the jQueryEash program that employs other jQuery features to make the turtles interact in more interesting ways. The following example builds off of the basic code used to create the two scripts illustrated in the lesson, using some additional variations on the basic jQuery selector syntax to make a battle between two armies of turtles:
Beyond the lesson
The lesson notes that use of anonymous functions can help avoid namespace collisions. For smaller programs, such as those needed to solve the various activities in this course, namespace collisions are relatively easy to isolate. Given their limited experience in coding, novice coders should be forgiven when they do not immediately appreciate the just how important a feature this is. However, in more extensive programs, especially when making use of multiple scripts or importing external libraries—both topics students will soon explore in subsequent PencilCoder lessons—namespace collisions are a major, and often vexing, source of coding bugs.
When used as part of a coding idiom known as an immediately invoked function expression (abbreviated IIFE, and pronounced "iffy"), anonymous functions can guarantee that our programs will be free of namespace collisions with variables from multiple scripts and/or external libraries. That is a very big deal indeed. We will return to the topic of anonymous functions, including a discussion of IIFEs, in the Closures! lesson.What can go wrong
plan
There a number of pitfalls that can occur when using plan
, but perhaps none more common that
Students confusing these issues will also misinterpret them as a variable scope issue; or they may with to "return" values from plan. However, that is not possible (nor is it logically feasable, given the aforementioned timing issues.)
Technicalities
plan
vs. queue
The plan
method is a Pencil Code enhancement to jQuery. It serves as an easier-to-use alternative to a built-in jQuery method, queue
. As described in the jQuery-turtle.js documentation, "[t]he plan
method can be used to queue logic (including synchronous tests or actions) by running a function in the animation queue. Unlike jQuery queue
, plan
arranges things so that if further animations are queued by the callback function, they are inserted (in natural recursive functional execution order) instead of being appended."
This script illustrates use of queue
and highlights the differing impact of each function on the animation queue. However, an extensive discussion of the built-in queue
method is beyond the scope of these notes. Interested readers are encouraged to seek out other resources, beginning with the jQuery API documentation.
Sprite-specific calls to plan
plan
is an alias for window.plan
. Sprite-specific code in the associated callback will be inserted in that specific sprite's queue, as it stands at that point of the execution of the overall script. In general, this is the way plan
will be called. However, as a jQuery method, plan
can be called on a specific jQuery object, using dot-notation.
Sprite-specific use of plan
can significantly impact the order/timing of resulting animations. Working through the logic can be tedious and beyond the scope of this lesson. Interested readers are encouraged to referenced this script which provides a simple illustration, with explanations, contrasting different variations of calls to plan
.
plan
versus each
When applied to collections of jQuery elements, e.g., $(".turtle").plan( cb )
, plan
iterates over the elements in the collection much like each
. In the context of iteration, it differs from each
in one important way: it sets the this
keyword to $(element)
instead of element
. That is, it automatically wraps this
in a jQuery object. This isa useful shortcut in Pencil Code, since the objects in jQuery collections usally consist of html page <canvas> elements used to represent sprites.
plan
: variable scope and timing issues
The Notes to the Activities stated that a challenge to working with plan
is that values generated through execution of the callback cannot be passed back to the rest of the program. However, that is a bit of an oversimplification. A more precise explanation is that if the values are from the containing scope, then those values are updated; it's just that they can't be updated in a way that affect most subsequent lines of code in the program.
The key to understanding nuances of plan
is that it's all about timing issues. plan
delays the execution of the code in the callback until a future point in time, determined by the animation, while the rest of the code in the program is executed immediately. This has some interesting consequences.
In the following variant of the previously provided example, the final statement of the script executes first (indeed, nearly instantaneously), with values that have been unaffected by the code inside of the callbacks (e.g., incrementing y
), though they do reflect the changes to variables outside of the callbacks. Meanwhile, the calls to see
within the callbacks illustrate two interesting features.
x = y = 0 for [1..5] fd 25 x++ plan -> y++ see "Inside of 1st plan: " + x, y plan -> see "Inside of 2nd plan: " + x, y see "Outside of plan: " + x, y
First, both calls to see
report the values of the global variables x
and y
at the time of the execution of the callback, i.e., after the completing of the animation preceding that call to plan, which is also after the completion of the rest of the script; that is, both a value of 5 for x
. Second, the values reported in the second callback reflect the updates to y
made in the first callback. This makes sense: the execution of the second callback happens after the completion of the all the code in the callback to the first call to plan
.