MultiLogo: A Study of Children and Concurrent Programming


Mitchel Resnick
Epistemology and Learning Group
The Media Laboratory
Massachusetts Institute of Technology
20 Ames Street Room E15-312
Cambridge, MA 02139
mres@media.mit.edu

Published in Interactive Learning Environments, vol. 1, no. 3 (1990).

Abstract

During the past decade, computer scientists have developed dozens of concurrent (or parallel) programming languagues. These languages hold the promise of opening up exciting new applications for computers. But that will happen only if people can effectively learn, use, and understand these new languages. To explore these issues, I developed a concurrent extension to Logo (called MultiLogo), and I conducted an experimental study with a group of elementary-school students. The students used MultiLogo to control simple robotic devices built out of LEGO bricks. In analyzing the children's work, I develop three primary categories of MultiLogo programming bugs: problem-decomposition bugs, synchronization bugs, and object-oriented bugs. Based on the results, I recommend ways to improve the design and teaching of concurrent programming languages for non-experts.

1. Introduction

In many computer applications, people need to control several objects at the same time. For instance, someone might want to program a group of graphical objects to move across a computer screen in a coordinated dance. Someone else might want to program several robotic machines to walk across the floor at the same time--perhaps in synchrony with a video image on the computer screen.

These coordinated activities, simple though they might seem, are very difficult (if not impossible) to program using traditional programming languages like Pascal or Logo. The problem is that traditional languages are based on a single process that executes instructions one at a time. This sequential paradigm does not match the way the real world works: people and animals act in parallel, objects interact in parallel. As a result, many real-world activities can not be modelled in a natural way with sequential programming.

The ideal solution is to use a concurrent or parallel programming language--that is, a language that allows programmers to control multiple, interacting processes.[1] During the past decade, computer-science researchers have developed dozens of concurrent languages (for an overview, see Gelertner (1986)). But there has been little empirical research on how people learn, use, and understand concurrent-programming languages. Such research is critical for improving the design and teaching of concurrent languages. There is a long list of unanswered research questions. For example: In what ways do these new languages simplify the task of programming? In what ways do they make the task more difficult? What factors influence the learning of concurrent programming? What existing mental models influence people's understanding of concurrent programming?

Some researchers expect that people will have difficulty learning to program with concurrent languages. Empirical studies have shown that novice programmers, using traditional sequential languages, have significant difficulties. In learning concurrent languages, novices must cope with many of these same difficulties plus a host of new ones, most notably issues related to synchronization of multiple processes. Indeed, some researchers worry that "the complexity of (concurrent) programming--all those processes active at once, all those bits zinging around in every direction--is simply too great for the average programmer to bear" (Gelertner, 1986).

On the other hand, some previous research seems to suggest that concurrent languages could facilitate the task of programming (at least in some situations). Several researchers (Pea et al. (1987), Bonar and Soloway (1985)) note that novice programmers (using traditional sequential languages) often assume parallelism where none exists. Would these novices find parallel programming more natural and intuitive?

To explore these issues, I developed a concurrent extension to Logo, called MultiLogo. MultiLogo provides new metaphors and constructs for controlling multiple processes at once. Its primary goal is to give people (particularly non-expert programmers) a simple yet powerful model for thinking about and programming concurrent processes.

I conducted an experimental study with a small group of elementary-school students. The students used MultiLogo to control the concurrent actions of robotic machines. In this paper, I analyze the students' work with MultiLogo, with special emphasis on the "bugs" in students' programs. Some of these bugs, no doubt, reflect weaknesses in the design of MultiLogo. Others, however, are likely to arise in all types of concurrent programming. I divide these general bugs into three primary categories, which I call problem-decomposition bugs, synchronization bugs, and object-oriented bugs. For each category, I discuss examples and possible causes of the bugs. This analysis provides a framework for understanding ways to improve the teaching and design of concurrent-programming languages.

2. Motivation

My work on MultiLogo was motivated, in large part, by my involvement in the development of a computer-based construction system called LEGO/Logo (Resnick et al., 1988). As students work on LEGO/Logo projects, they often want to control several robotic machines at the same time. After building a LEGO amusement park, for example, a child might want all of the rides to run simultaneously. To do that, some form of concurrent programming is needed.

As its programming language, LEGO/Logo uses an expanded version of Logo. Students can use any of the traditional Logo primitives and control structures (if, repeat, etc.), plus any of 20 new primitives added specially for the LEGO environment (such as on, off, and sensor?). To turn on a LEGO motor, the programmer first tells the computer which port on the LEGO interface box it should "talk to." Then, the programmer simply types on, and the motor turns on. A typical LEGO/Logo program is shown below. The program is designed for a LEGO car with touch sensors on the front and rear bumpers. The program turns on the car motor, then keeps checking each sensor. Whenever the car bumps into something, the program changes the direction of the motor (and thus the direction of the car).

to bumper-car
talkto :motor-port
on
check-sensors
end

to check-sensors
listento :front-sensor
if sensor? [rd] ;; rd is for "reverse direction"
listento :back-sensor
if sensor? [rd]
check-sensors
end

The LEGO/Logo programming language is adequate for a wide variety of applications--and, indeed, children have used it to program many different types of projects. But certain applications expose the limitations of the LEGO/Logo language. Consider, for instance, the check-sensors procedure shown above. Although this procedure works as desired, it presents certain conceptual difficulties for novice programmers. To understand the procedure, a programmer must think of the computer alternating between the two sensors--first checking the front sensor, then the back sensor, then the front sensor, and so on. A model in which each sensor is checked continuously and concurrently would probably be much more intuitive for novices, since it would more closely model the way "real" sensors (like eyes and ears) work. Indeed, in working with LEGO/Logo, many novices attribute some sort of intentionality to sensors. They seem to believe that each sensor plays an active role in checking itself, and in informing the computer (or the motor) when it has been pressed.

In other cases, the need for concurrency is even more compelling. Imagine that we want to modify the check-sensors procedure so that the computer flashes a light for five seconds every time the car reverses direction. We could write a subprocedure flash-light, then make the obvious modification:

to check-sensors
listento :front-sensor
if sensor? [rd flash-light]
listento :back-sensor
if sensor? [rd flash-light]
check-sensors
end

This procedure has a bug: what if the car bumps into something while the computer is flashing the light? The car will not reverse direction until after the computer has finished flashing the light. In short, the computer can not flash a light and check the sensors at the same time. We could modify flash-light so that it "time-slices" between the two activities, alternating between checking the sensor and flashing the light. But this "solution" has several drawbacks. When implemented in a high-level language, time-slicing is usually quite slow, so the light might flash irregularly, with a noticeable pause each time the computer checked a sensor. Equally important, this "fix" represents another step away from modularity; thus, it makes the programming task more conceptually complex.

This example reveals the need for what we might call check-and-act concurrency: a program needs to check for a condition and execute an action at the same time. Similar issues arise if we try to control two (or more) devices at the same time. For example, we might want a LEGO robot to walk around the room while a light on its head flashes continuously. This example calls for act-and-act concurrency: a program needs to execute two actions at the same time. As with check-and-act concurrency, there are sometimes ways to simulate act-and-act concurrency within the sequential paradigm, but at a cost of decreased modularity and increased conceptual complexity.

Some versions of Logo (and other languages) have offered programmers limited forms of concurrency in the form of sprites and demons (see Resnick (1988) for more details). These limited approaches are often useful, but they do not go far enough. They help in only certain specialized situations, and they do not provide a conceptually clean model of concurrency.

3. Design of MultiLogo

MultiLogo aims to provide a more general solution to the concurrency problem. It provides new constructs to help people think about and program all types of concurrent activity. This section describes the primary constructs of MultiLogo.

Agents. MultiLogo introduces a new object known as an agent. Each agent is like a separate version of Logo. To create concurrent activity, a MultiLogo programmer creates several agents, then asks each agent to execute Logo instructions at the same time.2 Besides executing traditional Logo (and LEGO/Logo) instructions, each agent can also communicate with other agents.

MultiLogo agents differ from "processes" in other concurrent-programming languages in that MultiLogo agents have stronger "identities." Users can create agents with distinctive "personalities," each with its own name, its own procedures, its own variables, and its own turtle. Agents were designed in this way so that programmers can more easily coordinate and keep track of the (concurrent) activities of various agents.

Communication. MultiLogo agents need to communicate with one another to share data and to synchronize their activities. They communicate primarily by sending messages to one another.[3] MultiLogo messages are in the form of expressions for the recipient agent to evaluate. For example, the message fd 40 tells the recipient agent to move its turtle forward by 40 units. The message print :name tells the recipient agent to print the object associated with its variable name. And the message start tells the recipient agent to run its start procedure.

Agents can send messages in several different ways. For example, an agent can demand another agent to evaluate an expression immediately, interrupting its current activity if necessary. Or the sender could ask the other agent to evaluate the expression "whenever it gets a chance."

To understand the differences between various types of communication, it is useful to imagine each agent maintaining a queue of expressions that it needs to evaluate. At any moment, the agent is evaluating the first expression in the queue. When an agent finishes evaluating an expression, it pops the expression off the queue and begins evaluating the next expression in the queue. If the queue becomes empty, the agent remains "dormant" until it receives another message.

Consider what happens when one agent demands another agent to do something. For example, an agent named manager might evaluate the following expression:

manager ==>
demand :flasher [onfor 20]

In this case, the message (onfor 20) gets pushed onto the front of flasher's queue. If flasher was in the process of evaluating an expression, the remainder of the evaluation is pushed down to the second position on the queue. After flasher finishes executing the message (presumably turning on a light for 20 time units), flasher resumes execution of the interrupted expression.

Consider, on the other hand, what happens if the manager agent asks (instead of demanding) that flasher turn on the light:

manager ==>
ask :flasher [onfor 20]

In this case, the message is placed at the back of flasher's queue. flasher executes the message whenever it completes executing all of the earlier expressions in the queue. So demand places messages at the front of flasher's queue, and ask places messages at the back of the queue. In either case, manager behaves the same: it simply sends a message to flasher, then continues with whatever else it is supposed to do. manager does not wait until flasher finishes executing the message; thus, ask and demand initiate parallel activity (assuming manager has more instructions to execute).

When an ask or demand message is evaluated by the recipient agent, the resulting value is of no use. In particular, the agent which sent the message has no access to the value of the message. Thus, messages sent with ask or demand can be considered "send-and-forget" messages. MultiLogo has two other communications primitives (demand-and-wait and ask-and-wait) which behave differently. After sending a message with one of these primitives, the sending agent waits until the recipient agent finishes evaluating the message. At that time, the recipient "sends back" the value of the message. Since the sending agent waits for a response, these types of messages are not used for initiating parallel actions. But they have important uses. For example, demand-and-wait can be used to inspect the current state of another agent, and ask-and-wait can be used to synchronize the actions of several agents (see Resnick (1988) for more details). The programming projects in this study focused on the ask form of communication.

Meta-level commands. Agents are always in one of three states: active, dormant, or paused. An agent is active if it is in the process of running a program. An agent is dormant if it currently has no program to run. An agent can influence the state of another agent by executing a meta-level command. For example, the command halt causes an active agent to become dormant.

Interface. This study used an early version of MultiLogo with a very modest user interface. The screen was divided into three windows. In one window, the user typed messages to the agents. Consider, for example, the following command:

manager ==>
forward 50

The prompt indicates the name of the agent (manager). This command asks the agent named manager to move its turtle forward by 50 "turtle steps."

A second window displayed printed output from the agents. The third window was for turtle graphics. As mentioned previously, each agent has its own turtle.

Example. Let's return to the example of the LEGO car with two touch sensors. We can create a MultiLogo agent for each sensor, and one for the car motor.[4] To make the car reverse direction whenever one of the sensors is pressed, we can write:

sensor1 ==>
listento :front-sensor
sensor1 ==>
repeat forever [if sensor? [ask :car [rd]]]

sensor2 ==>
listento :back-sensor
sensor2 ==>
repeat forever [if sensor? [ask :car [rd]]]

car ==>
talkto :motor
car ==>
on

Suppose (as in the earlier example) we also want a light to flash for a few seconds whenever a sensor is pressed. We can create a new agent named light to be in charge of flashing the light. Then we can change the instructions for sensor1 and sensor2 so that they send two ask messages: one to the car agent (asking it to reverse direction) and one to the light agent (asking it to flash for a few seconds).

4. Empirical Study: Methodology

I tested MultiLogo with students at the Hennigan Elementary School in Boston. Eight Hennigan students used the MultiLogo system during May and June 1987. In this paper, I focus on four of the students. This group included two fourth-grade students (one girl and one boy) and two fifth-grade students (one girl and one boy). Each of the students worked with MultiLogo for at least four sessions, with each session lasting between 45 and 90 minutes. I will refer to the students by their initials: FB, NL, DB, and SM. With one exception, I always worked with students on a one-to-one basis. (During FB's final session, she was joined by her classmate BW.)

All students had at least one school-year of experience programming in Logo, using a version of Logo called LogoWriter. (In fact, the students were part of a special computer-intensive environment, in which students worked at the computer for roughly an hour each day.) Two of the students (the two fifth graders) had some previous experience with LEGO/Logo; during the previous school year, they had each spent about a dozen one-hour sessions working on LEGO/Logo projects.

The MultiLogo sessions were in the form of semi-structured interviews. In a typical session, I suggested a particular project for the student to work on, and I explained any new programming concepts or primitives needed in the project. As the student worked on the project, I asked questions to probe the student's thinking. If the student ran into difficulties, I provided hints and suggestions. All sessions were tape recorded, and all computer interactions were saved in computer files.

During the study, I guided students through a sequence of projects. Each project involved writing a MultiLogo program to control a particular LEGO model. (The students did not build any LEGO models during the study; they were given fully-constructed models.) The sequence of projects served as only a rough framework; I did not follow the sequence blindly. If a student suggested a new idea for a project, I typically helped the student follow through on the idea (as long as the project seemed to touch on "interesting" concepts). Also, if a student had difficulty with a particular concept, I typically introduced an additional project to reinforce (and probe) the student's understanding of the concept.

The following sections describe the basic sequence of projects, with sample MultiLogo programs for each project.

Walker/flasher projects. The first group of projects involved a LEGO walking machine with a light on its "nose." These projects were designed to introduce students to MultiLogo's central metaphors (agents and communications). Through these projects, I hoped to analyze students' difficulties in learning and using these basic metaphors.

First, I asked students to make the machine walk back and forth. This task does not use any MultiLogo features; students could imagine that they were typing at a traditional LEGO/Logo interpreter. Then I asked the students to make the light flash while the walker was moving back and forth. This is a very difficult task using a traditional sequential approach. As students worked on this problem, I introduced the idea of MultiLogo agents.

After the students had worked with agents for a while, I asked them to make the walker and flasher start at the same time. The solution to this task requires communication among agents, so I introduced the MultiLogo primitive ask. There are two primary approaches to this problem. In the "hierarchical" approach, a manager agent sends messages to a walker agent and a flasher agent:

walker ==>
to walk
talkto :motor-port
repeat 6 [onfor 30 rd]
end

flasher ==>
to flash
talkto :light-port
repeat 20 [onfor 4 wait 2]
end

manager ==>
to walk-and-flash
ask :walker [walk]
ask :flasher [flash]
end

manager ==>
walk-and-flash

An alternate approach uses just two agents. In this case, the flasher agent sends a message to the walker agent, then starts its own flash procedure.

walker ==>
to walk
talkto :motor-port
repeat 6 [onfor 30 rd]
end

flasher ==>
to flash
talkto :light-port
repeat 20 [onfor 4 wait 2]
end

flasher ==>
to walk-and-flash
ask :walker [walk]
flash
end

flasher ==>
walk-and-flash

Touch-sensor projects. The next set of projects involved the use of LEGO touch sensors to control the walker/flasher. These projects were more difficult than the initial walker/flasher projects in several ways. First, the touch-sensor projects generally required the coordination of a larger number of agents. Second, problem decomposition tended to be more difficult in these projects: it was generally less obvious how many agents were needed and what each agent should do. Third, the projects required an understanding of the agents' queuing method for incoming messages.

To start, I reviewed the LEGO/Logo primitives sensor? and waituntil, and I gave several short examples of how to use touch sensors. Then I posed a task involving the LEGO walker plus two touch sensors. The goal was to write a program with the following behavior: if I pushed one of the touch sensors, the walker should walk back and forth several times; if I pushed the other touch sensor, the light should flash for a while. A solution to this task (making use of some procedures written in the previous section) might look like this:

walker ==>
listento :walk-sensor

walker ==>
to sense-and-walk
if sensor? [walk]
sense-and-walk
end

flasher ==>
listento :light-sensor

flasher ==>
to sense-and-flash
if sensor? [flash]
sense-and-flash
end

manager ==>
to sensor-task
ask :walker [sense-and-walk]
ask :flasher [sense-and-flash]
end

manager ==>
sensor-task

After students completed that task, I added an extra requirement: If I pushed the walker's touch sensor while the walker was walking, the walker should do another walking cycle when it finished its current cycle. That is, the walker should "remember" that its touch sensor had been pushed while it was walking. The LEGO light and its corresponding sensor should behave similarly.

To solve this task, students needed a more detailed model of agent communications. Most important, they needed to understand: what should an agent do if it receives a message while it is already active? I explained that each agent has its own queue and that new ask messages are placed at the end of the queue. (In some cases, I also introduced the primitives demand and demand-and-wait.) The new sensor task can be solved by dividing responsibilities among more agents. In the solution to the previous task, the walker agent was responsible both for checking a sensor and for making the walker move. These two jobs should be divided between two agents. That way, one agent can continue to check the sensor even while the walker is moving.

LEGO turtle projects. The final set of projects involved a LEGO "turtle." Whereas the LEGO walker has only one motor (allowing it to move forward or backward), the turtle has two motors (allowing it to turn right or left, as well as moving forward and backward). I introduced the turtle projects, in part, to observe how students coped with problem decomposition and synchronization in projects with two motors. In addition, the turtle projects were a natural setting for introducing meta-level commands like halt.

To start, students controlled the LEGO turtle using direct commands. Then, I asked the students to write standard "turtle" procedures to make the turtle go forward, backward, left, and right. For example, the procedure to make the turtle go forward might look like this:

left-wheel ==>
talkto :left-motor-port

right-wheel ==>
talkto :right-motor-port

lego-turtle ==>
to lego-fd :time
ask :left-wheel [clockwise onfor :time]
ask :right-wheel [clockwise onfor :time]
end

As a final task, I asked the students to write procedures to coordinate the motions of the graphics turtle and the LEGO turtle.

5. Empirical Study: Analysis of Results

In general, students appropriated the idea of agents sending messages to one another quite easily. By the end of four sessions, all of the students had successfully programmed the walker/flasher projects and the touch-sensor projects. Moreover, they seemed to grasp the power and usefulness of concurrent programming. One student (FB) described MultiLogo to a classmate in this way:

Remember how we were trying to do that program with the people walking and music and snow falling? Well, here there are different agents, and each agent can do something on the screen at the same time. You get it?

At times, however, students were confused by the new programming paradigm. As NL explained at the end of her first session:

It's sometimes confusing. You're asking two different agents to talk to two different things to do two different things. And so everybody's doing something else. And it's confusing to figure out what you are doing and why you are doing it and who told you to do it.

In my analysis of the results, I will steer away from the "success stories" and focus instead on students' mistakes and misconceptions, or "bugs." These mistakes can serve as clues to the thinking processes and mental models that students use during MultiLogo programming. My central purpose in analyzing students' bugs is to gain insights into the way students think about concurrent programming. (My analysis focuses exclusively on bugs related to concurrent programming. In writing MultiLogo programs, students made some mistakes unrelated to concurrent programming. I ignore such "traditional" bugs.) I divide the students' programming bugs into three primary categories:

* Problem-decomposition bugs. These bugs arise out of students' difficulties decomposing problems into actions to be performed concurrently by multiple agents.

* Synchronization bugs. These bugs arise out of students' difficulties coordinating and orchestrating the activities of multiple agents.

* Object-oriented bugs. These bugs arise out of students' confusion among different types of "objects" (e.g. agents, procedures, motors, ports) in the MultiLogo system.

These "bug categories" are not neatly disjoint. Bugs in one category interact with and influence bugs in other categories. For example, a programmer can not think about synchronization issues independently from problem-decomposition issues. Nevertheless, a rough division of bugs into categories is a useful first step towards understanding how novices think about MultiLogo. For each category, I will present examples from the students' work and speculate as to the causes and origins of the bugs. (In presenting students' programs, I sometimes "clean up" the code in order to illustrate a point more clearly. In doing so, I make every effort not to distort the spirit of the original code.)

Problem-Decomposition Bugs

Problem decomposition plays an important role in computer programming. To solve a complex problem, programmers typically divide the problem into simpler subproblems and write procedures to solve each of the subproblems. Each of these subproblems might be decomposed into yet simpler sub-subproblems, and so on recursively. This type of decomposition, which might be called functional decomposition, is useful not only in programming, but in all types of problem solving.

In concurrent programming, an additional form of problem decomposition is needed. While dividing a task into functional pieces, programmers must also decide how to divide the pieces among agents for concurrent execution. This aspect of decomposition, which might be called agency decomposition, does not replace functional decomposition. Rather, the two approaches are complementary; they represent two different dimensions in problem decomposition. Functional decomposition focuses on what needs to be done; agency decomposition focuses on who should do it.

Examples of problem-decomposition bugs. Two of the students (FB and NL) ran into problem-decomposition difficulties right away, in the initial walker/flasher task. FB used a standard functional decomposition approach: she began by writing two procedures called light and motor:

Agent 1 ==>
to light
talkto "a ;; light plugged into port a
onfor 20 wait 20
end

Agent 1 ==>
to motor
talkto "b ;; motor plugged into port b
onfor 40 rd
end

Notice that FB used only a single agent. Before writing the procedures, FB explained her approach:

I'll write one (procedure) for the light and one for the motor and put them together in one program so you can call the program by itself. Cause you can't repeat two programs and o them at the same time. You can repeat one program with two little programs in it.

But after writing the procedure, FB realized that she didn't know how to "combine" the two procedures so that they would execute at the same time:

You could do repeat 200 [motor light], but then it will do motor and then it will do light. You have to combine them. So how do you combine them? I want to leave these (subprocedures) alone, but I want to put them together. Without writing a procedure, putting them together... You said one agent does one thing, but if we combine them, then we could (do more than one thing at a time). But how do you combine two programs without writing one program for them all?

Eventually, all students in the study succeeded in decomposing the walker/flasher task into two loosely-coupled processes. But their grasp of agency decomposition was still quite fragile. This fragility was evident when they worked on the tasks involving the LEGO touch sensors. On the most advanced touch-sensor task (in which the walker and flasher must queue up incoming messages), all of the students (at least initially) had difficulty decomposing the task into "parallel" subtasks.

Consider, for example, the case of DB, probably the most proficient Logo programmer in the group. DB had little trouble with the walker/flasher activities, and he started with an agent-based approach for the touch-sensor tasks. He made one agent responsible for all walker-related activities, and he made a different agent responsible for all flasher-related activities:

Agent 1 ==>
to walk
talkto "b ;; motor plugged into port b
repeat 5 [onfor 30 rd]
end

Agent 1 ==>
to onwalk
listento :walker-sensor
ifelse sensor? [walk onwalk] [onwalk]
end

Agent 2 ==>
to light
talkto "a ;; light plugged into port a
repeat 10 [onfor 20 wait 20]
end

Agent 2 ==>
to onlight
listento :light-sensor
ifelse sensor? [light onlight] [onlight]
end

Agent 1 ==>
to sense
ask 2 [onlight]
onwalk
end

This program worked well. The decomposition into two agents played an important role: the walker could be started even while the light was flashing, and vice versa. This feature would be difficult to implement with traditional Logo. But then I pushed the walker's touch sensor while the walker was moving. It had no effect. I suggested that DB change the program so that the walker could "remember" if its touch sensor had been pushed while it was walking; if it had, the walker should do another walking cycle.

DB understood the problem immediately: "Ah. That's tricky. So it has to keep looking for that (the sensor) even when it's doing the walk." To solve this problem, however, DB resorted to his sequential time-slicing instincts. He explained his plan for the flasher:

It's going to flash the light on and off, check the sensor, then flash the light on and off... So before it turns the light on every time, it's going to check the sensor, whether it's been pushed again.

He changed his light procedure:

Agent 2 ==>
to light
talkto "a
repeat 10 [if sensor? [light2] onfor 20 wait 20]
end

DB explained that light2 would "do the same thing" as light itself. That is, it would make the light flash 10 times (on for two seconds, off for two seconds), checking the sensor between each four-second flash cycle. (In fact, DB could have simply used light in place of light2.) Thus, DB's light procedure time-slices between flashing and checking. This approach, while clever, has a flaw: what if I press and release the sensor during one of the flash cycles? Then the procedure will never "know" that the sensor was pushed. When I posed this problem to DB during the next session, he quickly came up with a solution: "We can do it (the flash) for only one second." That is, use finer time slicing.

That is a clever patch. Indeed, it adopts the same approach that I used in writing the underlying software to simulate multiple processes. But the patch has problems. It is not possible to write fast time-slicing routines in Logo itself (since Logo, like other "high-level" languages, is quite slow). So DB's procedure might still "miss" the push of the sensor. Moreover, DB's solution is much more complicated than it need be, and it would be very difficult to modify or extend. Using concurrent-programming constructs, it is possible to write a much simpler, more modular solution. (Eventually, DB developed an excellent solution to the touch-sensor task. His solution used seven different agents: two to check the sensors, one to run the walker, one to run the flasher, and three to start the whole thing going.)

Causes of problem-decomposition bugs. In thinking about problem decomposition, some students seemed stuck in the sequential paradigm. They continued to think in terms of "a little for this agent, a little for that agent." This sequential, time-slicing approach was particularly evident (and deeply entrenched) in NL's and FB's thinking. I asked NL and FB to explain how they would coordinate a "real-life" situation in which two people must execute tasks simultaneously, and (at least initially) they both stuck with their time-slicing approaches. Consider this excerpt from my conversation with NL:

M: Let's forget about the computer. Let's say you were going to wash some dishes and I was going to brush my teeth. How would you set it up? So I'm standing at a sink.

N: And I'm standing at a sink.

M: What are you going to do?

N: I'm going to wash a dish. Wash, wash, wash. (To me:) Brush your teeth. And you're going to brush your teeth. And I'm going to wash another dish, and I'm going to tell you to brush your teeth again.

My conversation with FB was very similar. I asked her to "program" a situation in which she had to sweep the floor and her brother had to set the table for dinner:

F: I'm going to tell him to start setting the table, which he won't do. Anyway, he starts on the table, then I go back and start sweeping.

M: So you're going to ask him to set the table, and then you're going to start sweeping.

F: But when I tell him to do it, he only does one plate. But there are five people that he has to set places for and he only sets one plate. Because he has to repeat this thing. And I have to repeat. I only make one stroke with my broom. But we have to repeat it forever. But there is no way both of us...

When pressed, NL and FB quickly recognized and admitted the nonsensical nature of their "programs." Indeed, pressing students to think about "real-life" models proved to be a good way to nudge them towards a "parallel" programming style. But NL's and FB's initial "solutions" to the real-life problems are an indication of just how strong their sequential-thinking models are.

It is possible that some students had trouble decomposing problems into concurrent subtasks precisely because of their expertise in traditional programming. The students in the study were among the best Logo programmers in the school; they could quickly appeal to standard "cliches" of traditional programming (such as time-slicing and functional decomposition). Unfortunately, these cliches sometimes got in the way as the students tried to master the new programming paradigm.

BW, who worked together with FB during FB's final session, provides an interesting contrast. BW, though a good student, had limited Logo programming skills and had never seen MultiLogo. Nevertheless (or perhaps as a result), her instincts on agency decomposition were quite good. At the start of the session, FB (a veteran of three MultiLogo sessions) gave BW a short MultiLogo tutorial. Then I posed the touch-sensor task. BW suggested creating new agents for each of the sensor-checking procedures, an excellent suggestion that FB ignored. It is possible that BW's lack of Logo skills made it easier for her to embrace the new paradigm. BW would have had great difficulty writing the program to implement her idea, but she grasped the overall concept of concurrent programming very quickly.

Synchronization Bugs

Perhaps the most pervasive MultiLogo bugs involved the synchronization of multiple agents. These bugs differ from the problem-decomposition bugs discussed in the previous section: in a prototypical synchronization bug, a student successfully decomposes a problem into subtasks for different agents, but then has difficulty coordinating the activities of those agents.

Examples of synchronization bugs. Students' synchronization bugs tended to fit into two broad categories: unintended sequentiality and unintended concurrency. In an unintended-sequentiality bug, the programmer attempts to make two (or more) agents act concurrently, but gets sequential behavior instead. The simplest (and most common) example involved the initial walker/flasher activity. Three of the four students in the study initially wrote procedures similar to these:

Agent 1 ==>
to walk
repeat 100 [onfor 80 rd]
end

Agent 2 ==>
to flash
repeat 200 [onfor 10 wait 10]
end

Agent 2 ==>
to walk-and-flash
flash
ask 1 [walk]
end

Agent 2 ==>
walk-and-flash

As written, agent 2 will make the light flash 200 times, then it will ask agent 1 to make the walker move forward and back 100 times. The program is still functioning sequentially; the walk-and-flash procedure does not exploit the potential for concurrency. To make the two activities (walking and flashing) occur concurrently, the two lines in the walk-and-flash procedure should be reversed: agent 2 should ask agent 1 to make the walker move, then (while agent 1 is doing that) agent 2 should make the light flash. (It is somewhat ironic that proper concurrency, at least in this case, requires proper sequencing.)

Similar difficulties arose in more advanced activities. NL stumbled into a three-agent version of this bug when she tried to coordinate the actions of the screen turtle and the LEGO turtle. At first, NL wrote:

Agent 1 ==>
to both-fd :num
ask 2 [talkto "b onfor :num] ;; one motor in port b
talkto "a onfor :num ;; other motor in port a
fd :num
end

This procedure works quite well, but the screen turtle doesn't move until after the LEGO turtle is finished (since agent 1 controls one of the LEGO turtle wheels and the screen turtle). To fix this bug, NL realized (correctly) that she needed a third agent. But she added the third agent like this:

Agent 1 ==>
to both-fd :num
ask 2 [talkto "b onfor :num]
talkto "a onfor :num
ask 3 [fd :num]
end

The program now uses the screen turtle of a different agent, but the screen turtle still does not move until the LEGO turtle finishes. NL ultimately solved the problem by placing the screen-turtle command (ask 3 [fd :num]) before the LEGO-turtle commands.

In unintended-concurrency bugs, the problem is the reverse: the programmer wants two actions to be sequential, but they end up concurrent instead. DB encountered a subtle unintended-concurrency bug while programming the LEGO turtle. He began by writing these turtle procedures:

Agent 1 ==>
to trt
talkto "a ;; left motor in port a
onfor 30
end

Agent 2 ==>
to tlt
talkto "b
;; right motor in port b
onfor 30
end

Agent 3 ==>
to tfd
ask 1 [trt]
ask 2 [tlt]
end

The trt (for turtle-right) procedure turns one of the turtle's wheels for three seconds--making the turtle turn in an arc. tlt (for turtle-left) turns the other wheel for three seconds--making the turtle turn in the opposite direction. tfd (for turtle-forward) runs the trt and the tlt procedures concurrently--making both wheels turn so that the turtle moves forward. These procedures, when used individually, all worked fine. Problems arose when DB wrote a square procedure:

Agent 3 ==>
to square
repeat 4 [tfd ask 1 [trt]]
end

This procedure looks analogous to a traditional turtle-graphics square procedure. But the procedure does not work: instead of tracing out a square, the turtle goes forward for 12 seconds, then turns right for 12 seconds. To understand the problem, consider what happens when agent 3 executes the square procedure. Agent 3 executes the following sequence of commands:

tfd
ask 1 [trt]
tfd
ask 1 [trt]
tfd
ask 1 [trt]
tfd
ask 1 [trt]

But in executing tfd, agent 3 simply sends two messages: a trt message to agent 1 and a tlt message to agent 2. So in executing square, agent 3 does nothing other than send messages. Agent 3 sends 12 messages in all--eight messages from the four tfd commands, and four messages from the four ask 1 [trt] commands. Importantly, agent 3 does not pause between sending these messages. After sending the two messages as part of its execution of tfd, agent 3 immediately sends a trt message to agent 1. And after sending the trt message to agent 1, it immediately executes the next tfd and sends two more messages. And so on.

All of these messages get queued up by agents 1 and 2. Agent 1 gets eight trt messages in all; agent 2 gets four tlt messages. Agents 1 and 2 gradually work through their queues. For a while, both agents have messages to execute; agent 1 executes trt while agent 2 executes tlt, so the LEGO turtle goes forward. After 12 seconds (four tlt commands of 3 seconds each), agent 2 runs out of messages in its queue. Agent 1 continues to execute trt messages by itself, and the turtle turns to the right. DB ultimately solved this synchronization problem by changing the tfd procedure so that it waits until one of its "sub-agents" is finished:

Agent 3 ==>
to tfd
ask 1 [trt]
demand-and-wait 2 [tlt]
end

Causes of synchronization bugs. There seem to be two major causes of synchronization bugs: failure of the conversational metaphor and assumption of "excessive parallelism."

* Failure of the conversational metaphor. Novice programmers often make use of what Pea (1986) calls the "conversational metaphor"--that is, they imagine themselves having a conversation with the computer (or, in the case of turtle graphics, with the turtle). This metaphor is deeply rooted, Pea notes, since the programmer "has communicated throughout an entire lifetime in a conversational manner." The conversational metaphor can be very helpful in thinking about programming, but it also leads to some misconceptions. Indeed, Pea argues that many novice programming bugs are caused by programmers' attempts to generalize from natural-language conversations to programming conversations. For example, some novice programmers expect the computer to have human-like interpretive capacities, going beyond the information given in the code. In effect, they expect the computer to "read between the lines," just as a good human listener does.

In MultiLogo programming, the conversational metaphor is even more problematic. MultiLogo "conversations" are different not only from natural-language conversations, but also from Logo "conversations." In a typical Logo conversation, the roles are clear: the programmer is the initiator, and the computer/turtle is a relatively passive recipient. The programmer gives commands, and the computer/turtle executes them (and, perhaps, sends back a response). In a MultiLogo interaction, the situation is much different. After the programmer gives a command to an agent, the agent might send a message to another agent, which, in turn, might send a message to yet another agent (or back to the first agent), and so on. An agent, unlike the computer/turtle of sequential Logo, is not solely a recipient. Each agent can play a dual role, both initiating and receiving commands.

As a result of these differences, programming in MultiLogo requires new metaphors. Writing a MultiLogo program is more like orchestrating a conversation among others as opposed to having a dialogue with another person. Unfortunately, children typically have little experience orchestrating conversations (or other actions) among groups of people. They have few metaphors or models to draw upon. So they tend to apply more traditional conversational models--often with buggy results.

The failure of the traditional conversational metaphor is manifested in several different ways. Consider, for example, the standard synchronization bug in the walker/flasher procedure:

Agent 2 ==>
to walk-and-flash
walk
ask 1 [flash]
end

For at least two students (FB and NL), this bug seemed to arise (at least in part) from an inappropriate extension of the conversational metaphor. FB, for example, explained the procedure like this: "Well, you start one, then you ask the other one to start." Rather than orchestrating the interaction between agent 1 and agent 2, FB apparently imagined herself having conversations with each of the two agents. In this way, FB could view each agent as a passive (that is, non-initiating) recipient, much like the turtle/computer in traditional Logo programming. Since a different agent received each of the two commands, FB expected that the commands should run concurrently.

Interestingly, both FB and NL found the walker/flasher task much easier when I suggested that they add a third agent to coordinate the activities of the other two. NL, for example, quickly typed a procedure of the form:

Agent 3 ==>
to walk-and-flash
ask 1 [walk]
ask 2 [flash]
end

This approach fits much more naturally with the traditional Logo conversational metaphor. Each agent has one clearly defined role. Agents 1 and 2 act like traditional turtles, carrying out instructions; agent 3 is an initiator, like a Logo programmer. Moreover, the hierarchical structure of the agents in the walk-and-flash procedure seems to be a much more familiar organizational structure for the students; even in elementary school, children have already dealt with many hierarchies. NL, for example, quickly labelled the third agent as "the teacher." FB used a similar metaphor. She imagined herself and her brother as agents 1 and 2. Then she added: "My mom would be agent 3."

The conversational metaphor also falls short along another dimension: it does not provide a strong model for situations in which one agent wants to communicate with another, but the recipient is already "busy." In real-life conversations, this situation is unlikely since we get sensory feedback. We know if another person is already talking or otherwise occupied. In Logo "conversations," we never need to worry: if the turtle/computer is busy executing instructions, we can't even type any new instructions. Thus, many programmers are totally unprepared for MultiLogo-style interactions among agents. They are unaccustomed to thinking about whether the recipient of a message might be busy. Consider, for example, DB's attempts to program the LEGO turtle to walk in a square (described early in this section). In orchestrating messages among agents, DB never seemed to think about whether a message's recipient might already be executing a message.

* Assumption of "excessive parallelism." In writing programs in traditional sequential languages, novices sometimes exhibit what Pea (1986) calls "parallelism bugs." That is, they assume that several lines of a program can be "active" or executing at the same time. Soloway et al. (1981), for example, found that 34% of the students in an introductory Pascal course assumed a type of parallelism in the execution of a while loop: they thought that the program continuously monitored the while test condition as it executed the body of the loop, exiting from the loop as soon as the test condition became true. Similarly, Pea (1986) found that more than half of the students in one high-school BASIC course expected if-then conditional statements to act as demons, executing whenever the conditional predicate became true.

Students programming in MultiLogo exhibited a related bug: they sometimes assumed that a single agent could do more than one thing at a time. In some cases, the bug manifested itself in familiar ways. For example, some students assumed that an agent could continue to check an if statement while performing other actions. But the bug also had new manifestations in MultiLogo, making it even more prevalent than in traditional Logo programming. It was very common, for example, for students to think that an agent could send messages and perform other actions at the same time.

In at least some cases, this assumption of parallelism within an agent seemed to be an underlying cause for the walker/flasher synchronization bug:

Agent 1 ==>
to walk-and-flash
walk
ask 2 [flash]
end

After NL wrote a procedure similar to this, I asked her to explain the meaning of the line ask 2 [flash]. She replied: "Can you please do this for me? I'm busy walking." Clearly, she imagined that agent 1 sent the message while it was walking.

FB had a similar misconception about an agent receiving a message: she thought that the agent (if busy) could continue with its current activity and execute the message at the same time. In one particular case, an agent was making a LEGO light flash, when another agent demanded that the agent execute the message onfor 100. FB predicted that the light would go on for 10 seconds, then resume flashing. This prediction was correct, but her reasoning was flawed. Her explanation:

This is how I thought about it. The procedure was running. Then that little guy who goes around and picks people, found out that you wanted something else to happen. This is the light, going on every other minute. But then over here it has another whole procedure, and it wants the light on. And it does it. And it meets with this (the flashing procedure) but it also fills in the times when it's off. So it will run continuously together.

Clearly, FB imagined that the agent was executing both procedures (the flashing and the onfor 100) at the same time. In her mind, the onfor 100 command "fills in" and turns on the light for the periods during which the flashing command would have otherwise turned the light off.

The excessive-parallelism bug seemed relatively robust, persisting even in the face of my repeated warnings that agents can do only one thing at a time. What factors could cause such a robust bug? Most analyses of parallelism bugs have focused on novices' use of natural-language interpretations for programming commands. Bonar and Soloway (1985), for example, explain the Pascal "while demon" bug like this:

In natural language, while is typically used as a continuously active test... This kind of control structure is unusual in a programming language. More typical is a construct in which the loop condition gets tested once per loop iteration (e.g., the while loop in Pascal). The surface link between the two kinds of "while" allows a novice to infer similar semantics.

This analysis provides a reasonable explanation for parallelism bugs in sequential languages. But it does not adequately explain students' excessive use of parallelism in MultiLogo. In MultiLogo, students' parallelism bugs do not appear to be linked to misinterpretation of particular words (like while or if). Other factors seem to be at work.

In some cases, the bug was probably caused by the students' over-generalization of a new concept. When a new concept is introduced, students are likely to use it (at least initially) in some inappropriate contexts. In this case, students tended to overuse the new idea of concurrency, applying it (inappropriately) to individual agents.

The use of LEGO devices might also have contributed to the strength of the bug. Consider a situation in which an agent turns on a LEGO motor (or light), then performs some other action. Some students seemed to think that the agent needed to perform some continuing action in order to keep the motor (or light) on. This model gave students the false impression that the agent was doing two things at the same time.

Probably the most important factor, though, was the way students identified agents with people. I encouraged this identification in my explanations, and students quickly adopted the metaphor. But this personification of agents had an unintended side effect. Some students seemed to reason that agents, like people, should be able to do several things at the same time. Indeed, students were most likely to attribute multiple simultaneous actions to a single agent precisely when the agent was performing actions that a person could do simultaneously. The misconception that an agent should be able to simultaneously perform an action and send a message (i.e. talk to someone else) is a case in point; people can certainly walk and talk at the same time.

This factor was highlighted when I asked NL to think about a "real-life" situation in which she had to wash some dishes and I had to brush my teeth. How would she "program" these actions? She replied: "I'm going to do the dishes, and while I'm doing a dish I'll tell you to brush your teeth" (emphasis added). If people can do it that way, why not agents?

Object-Oriented Bugs

MultiLogo includes many different types of objects: agents, turtles, ports (on the LEGO interface box), motors, sensors. This multitude of objects sometimes caused confusion for the students in the study. The students did not seem to have a clear model of which commands influenced which types of objects, or how each type of object interacted with the others.

These confusions led to a collection of bugs that I call object-oriented bugs. Although some of these bugs might arise in traditional Logo programming (particularly in LEGO/Logo), they seem far more common in MultiLogo programming, due to the greater variety of object types.

Examples of object-oriented bugs. The most common object-oriented bugs involved confusions between agents and other objects. In particular, students often mixed up which commands affected agents, and which commands affected other objects (such as motors). This confusion was most evident when students wanted to stop objects. In almost all cases, students had difficulty understanding the difference between stopping an agent, stopping a procedure, stopping a motor, and stopping a turtle. In FB's first session, for example, she programmed agent 1 to make the LEGO walker move forward and back several times:

Agent 1 ==>
repeat 100 [onfor 50 rd]

When FB wanted to stop the walker, she typed:

Agent 2 ==>
halt 1

This command halts agent 1--but it does not turn off the motor that agent 1 was controlling. So the walker keeps moving (although it no longer reverses direction). This result surprised FB, but she quickly came up with an explanation: she must have told the agent to halt while it was executing the rd command. In her mind, the halt command stopped only the rd; the onfor command was still working, so the walker continued to move.

SM also had problems when he tried to stop the walker. He started the walker with a repeat instruction similar to the one that FB used. When he wanted to stop the walker, he typed off. The motor turned off, and SM thought he was done. But a few seconds later, the motor turned back on, much to SM's amazement. The reason: the off command simply turned off the motor, it did not halt the agent. The agent was still running its repeat instruction. So the next time the agent executed an onfor command (within the execution of the repeat instruction), the motor turned back on.

I explained the problem to SM, and he came up with an alternative solution: he told the agent to halt. But that led to the same problem that FB had: the agent stopped, but the motor kept going. In fact, to stop both the agent and the walker, you must give a sequence of two commands, and the commands must be in the proper order. First you must halt the agent, then tell the agent to turn off the motor.

NL had similar problems with a turtle project. Her problems resulted from a confusion between agents and turtles. She wanted to use two LEGO touch sensors to control a screen turtle (with each sensor making the turtle turn in a different direction). But NL had difficulties, largely because she continually confused agents and turtles. She wanted to use two agents (one for each touch sensor) to control one turtle. But the concepts of agents and turtles were so closely associated in NL's mind that she had trouble thinking about them separately. Both in her comments and her code, she kept referring to turtles when she meant agents, and vice versa.

Causes of object-oriented bugs. It is not surprising that the students had some difficulty distinguishing between different types of objects. In their previous Logo experience, the students never needed to make such distinctions. They could, for instance, think of "Logo" and the "turtle" as the same thing. To stop a graphics procedure, it didn't matter if they thought about "stopping the procedure" or "stopping Logo" or "stopping the turtle." The results were the same.

This type of thinking, however, leads to problems in MultiLogo programming. NL, for instance, had a particularly strong association between "Logo" and "the turtle." In the first session, when I said something about typing commands "to Logo," she interrupted: "I like to think of it as talking to the turtle." In fact, she had even given a name (Harry) to the turtle, and she referred to the turtle by its name. This close association between Logo and the turtle can be very useful in traditional Logo programming. But the close association probably made it difficult for NL to separate the concepts of agent and turtle in MultiLogo.

These problems were probably exacerbated by my decision to allow students refer to agents by number, rather than giving the agents names. With agents identified only by number, it was easy for students to confuse agents with turtles (which are identified by number in LogoWriter), or with ports on the interface box (which are identified by letters and numbers). If the students had given agents names, they might have been more likely to view the agents as distinct entities.

The distinction between agents and interface-box ports was further confused by the fact that both objects seem to be involved in some type of communication. Agents send messages to one another (using ask and demand), but agents also send messages (like on and off) to interface-box ports. Thus, the roles of agents and ports can easily be confused.

6. Lessons

The empirical study revealed certain common problems for novice MultiLogo programmers--over-generalization of parallelism; misuse of the conversational metaphor; continued reliance of sequentialist techniques; over-personification of agents; confusion among various types of objects. These same problems (or closely related variants) will likely arise as novices begin to use other concurrent-programming languages. Some of these difficulties could be eased by changes in the design of concurrent-programming languages. But the learnability of a programming language is not a property of the language alone; the way in which the language is taught is equally important. So changes in pedagogy might be needed. Below, I discuss possible changes in design and pedagogy, based on the three categories of concurrent-programming bugs.

Problem-decomposition bugs. When students had difficulty decomposing problems into subtasks for separate agents, I often asked them to write informal "programs" to solve "real-life" problems, such as household chores. This tactic seemed to work well. Some students initially gave solutions heavily influenced by "sequentialist" thinking, in effect time-slicing between different people rather than having the people work in parallel. But the students quickly recognized the nonsensical nature of the time-slicing approach, and they developed new solutions based on concurrent actions by different people. Moreover, the students were able to transfer this idea to the programming domain, solving programming tasks that had earlier presented difficulties.

In future use of MultiLogo, it would be a good idea to make even greater use of such "real-world" examples. These examples should be supported with discussions about different approaches to problem decomposition. It is important for students to gain an understanding of the differences between functional decomposition and agency decomposition, and a better sense of how and when to use each type of decomposition.

Synchronization bugs. To work through synchronization bugs, it is a good idea for students to "play agent"--that is, act out what each agent is supposed to do. This activity requires a group of students, each playing the role of a different agent. Messages can be represented by pieces of paper that agents deliver to one another. Each agent/student should carry some sort of box or folder to hold a queue of messages. By acting out MultiLogo programs in this way, students can see how MultiLogo programs are much more like orchestrating a conversation among others rather than having a dialogue with another person. Thus, they can begin to develop a broader version of the conversational metaphor. Students can also confront their "excessive-parallelism" tendencies by carefully acting out their scripts, making sure to do only one thing at a time. After acting out a program, students should trade roles and act it out again, so that each student can see what the process looks like from different perspectives. This tactic could help students develop an ability to "decenter"--that is, put themselves in the place of different agents.

An alternate approach, of course, is to change the design of MultiLogo to match students' preconceptions. For example, I could redesign MultiLogo agents so that each agent could do several things at the same time, in line with students' expectations of "excessive parallelism." Underlying this issue is a tension that exists in all types of design: should the design conform to people's intuitions, or should the design encourage people to develop new intuitions? This question, of course, has no simple answer; the question must be reconsidered with each design decision. In future research, it would be interesting to explore different designs for MultiLogo agents, some supporting "excessive parallelism," others not.

Object-oriented bugs. Perhaps the most important antidote to object-oriented bugs is a more explicit introduction to the various types of objects in MultiLogo (agents, turtles, motors, etc.). In introducing MultiLogo, it is important to discuss which commands affect which objects, and how various objects relate to one another.

Changes in the design of MultiLogo's agent construct could also be helpful. MultiLogo agents are designed to be as general as possible. Each agent starts out identical; it is up to the user to give each agent particular features. For example, if a user wants one agent to control the motor of a car, the user can customize the agent as follows: name the agent something like car-motor, plug the car motor into port A on the interface box, and type the command talkto "a to the car-motor agent. Then any command typed to the car-motor agent will control the car motor.

This approach provides the programmer with a great deal of flexibility. But it can also lead to confusion. Some students (understandably) got confused between the talkto command for choosing a port on the interface box, and the ask and demand commands for sending messages to agents. The problem is that the message-passing metaphor is being used in two different ways: in one case to refer to messages sent from one agent to the interface box, in the other case to refer to messages sent from one agent to another.

One way to avoid this confusion is to create specialized agents--in this case, agents designed to talk to specific ports on the LEGO interface box. For example, one particular agent would be responsible for all instructions sent to port A of the interface box. Perhaps the agent would be named port-a. So to control a LEGO device plugged into port A, you would simply send the appropriate message to the port-a agent. This approach unifies the two forms of communication; the talkto command could be eliminated.

Similarly, it is not clear that all agents should have personal turtles. It would be worth experimenting with a version in which certain specialized agents controlled the screen turtles. Those specialized agents could be seen as turtles, not as having turtles. With this approach, the confusion between agents and turtles might be reduced.

7. Future Directions

My work with MultiLogo raises as many questions as it answers. Among the possible directions for future research:

Empirical studies. My initial empirical study suggests many possible follow-ups. For one thing, it would be interesting to observe non-experts as they gain more experience and work on more complex MultiLogo programs. What programming difficulties would be most persistent? What new difficulties would arise? In addition, it would be worth investigating how previous programming experience (with traditional sequential languages) affects a person's ability to learn concurrent programming. My initial study gave mixed indications on this question. Finally, it would be interesting to study how children apply concurrent programming concepts and metaphors to other domains. Does experience with MultiLogo affect the way they approach other complex (but non-computer) design tasks? Does it affect the way they think about multi-object physical systems (like the solar system)? Does it affect the way they think about their own minds?

Other models of concurrency. MultiLogo represents just one approach for introducing concurrency into programming. MultiLogo is well-suited for certain concurrent programming tasks (such as robotic control), but it is ill-suited for others. For example, it would be difficult to use MultiLogo to model behaviors involving hundreds or thousands of concurrent processes--such as the flocking behavior of birds or the foraging behavior of an ant colony. I am currently developing a new concurrent language for these types of applications (Resnick, 1990). This new language, tentatively called *Logo, is being implemented on the Connection Machine, a massively-parallel computer with thousands of processors. With this new language, I hope to explore how people (particularly children) learn new ideas about emergent and self-organizing behaviors.

These types of projects can lead to new insights into how people learn, use, and understand concurrent programming. Such insights are critical in a new field like concurrent programming. If concurrent languages are difficult to learn and use, they will never have a widespread impact. Indeed, the growth of the concurrent programming could ultimately be paced by the ease with which programmers are able to learn and use the new ideas of concurrency.

Acknowledgments

Hal Abelson and Seymour Papert provided intellectual guidance in the design, development, and testing of MultiLogo. Chris Hanson, Bill Rozas, and Andy Berlin (of the MIT AI Lab) and Allan Toft (of the LEGO Group) generously shared their time and expertise in helping me implement MultiLogo. Edith Ackermann, Mike Eisenberg, Andee Rubin, Brian Silverman, and Franklyn Turbak provided helpful comments throughout the course of the project.

The LEGO Group, the National Science Foundation (Grants 851031-0195, MDR-8751190, and TPE-8850449), and the General Electric Foundation have provided financial support for my work with MultiLogo.

References

Bonar, J., and Soloway, E. (1985). Preprogramming Knowledge: A Major Source of Misconceptions in Novice Programmers. In Human-Computer Interaction, vol. 1, pp. 133-161.

du Boulay, B., O'Shea, T., and Monk, J. (1981). The black box inside the glass box: Presenting computing concepts to novices. In International Journal of Man-Machine Studies, vol. 14, pp. 237-249.

Gelertner, D. (1986). Domesticating Parallelism. In Computer, vol. 19, no. 8.

Goldberg, A., and Robson, D. (1983). Smalltalk-80: The Language and its Implementation. Reading, MA: Addison-Wesley.

Hewitt, C. (1977). Viewing control structures as patterns of passing messages. In Journal of Artificial Intelligence, vol. 8 (3) pp. 323-364.

Kay, A. (1986). Personal conversation.

Mayer, R. (1981). The Psychology of How Novices Learn Computer Programming. In ACM Computing Suryveys, vol. 13 (1).

Papert, S. (1980). Mindstorms: Children, Computers, and Powerful Ideas. New York: Basic Books.

Pea, R.D. (1986). Language-independent conceptual `bugs' in novice programming. In Journal of Educational Computer Research, vol. 2 (1) pp. 25-36.

Pea, R.D., Soloway, E., and Spohrer, J.C. (1987). The Buggy Path to the Development of Programming Expertise. In Focus on Learning Problems in Mathematics, winter 1987.

Resnick, M. (1988). MultiLogo: A Study of Children and Concurrent Programming. Unpublished Master's Thesis. Cambridge, MA: MIT Dept. of Electrical Engineering and Computer Science.

Resnick, M., Ocko, S., and Papert, S. (1988). LEGO, Logo, and Design. In Children's Environments Quarterly. vol. 5 (4). New York: City University of New York.

Resnick, M. (1990). *Logo: A Computational Environment for Exploring Self-Organizing Behavior. Unpublished PhD Thesis Proposal. Cambridge, MA: MIT Dept. of Electrical Engineering and Computer Science.

Soloway, E., et al. (1981). Programming and Cognition: Why Your Students Write Those Crazy Programs. In Proc. of the Nat. Educ. Computing Conf., pp. 206-219.