PencilCoder

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:

Code

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 the saveimg 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:

    Code

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.