Hour 11: Actions and Classes - Part 1

This lesson will introduce you to the basics of actions and classes in Quorum.

Overview

In previous lessons, you have seen how to use objects from libraries and how to call actions from those objects. In this lesson, you will learn how to make your own. The first part will focus on learning how to create and use actions.

Goals

You have the following goals for this lesson:

  • Learn how to use "action" blocks to create an action
  • Learn about the optional properties of actions: parameters and return values
  • Discuss how "scope" impacts variables declared in and out of actions

Warm up

Actions allow programs the ability to perform a task. In the previous lesson, while you were working on Form apps, recall when you were using block commands:

page:AddBanner("banner1", "About me!", "Welcome reader to my about me page!")
page:AddButton("Click me!")

Each of these calls are an action. Why might programmers write these for us instead of having us write them all ourselves?

Vocabulary

You will be learning about the following vocabulary words:

Vocabulary
TermDefinition
ActionA named sequence of code that can be reused later in a program.
Action CallCode that tells an action to run.
ParameterA special variable inside an action. It must be provided as an input when the action is called.
Return ValueThe result of an action, which can be used like a variable.
ScopeThe area where a variable exists and can be used.
ClassA structure that defines a custom Object type. It can contain variables and actions.

Code

You will be using the following new pieces of code:

New Code to Learn
Quorum CodeCode ExampleExplanation
action NAME
end
action AddNumbers
end
Creates an action/function inside of Quorum.
action NAME(TYPE VARIABLE_NAME)
end
action AddNumbers(integer a, integer b)
    value = a + b
end
Creates an action that uses parameters, letting you pass in values from the main program to be used in the action.
action NAME returns TYPE
end
action AddNumbers(integer a, integer b) returns integer
    value = a + b
    return value
end
Creates an action inside of Quorum that returns a value back once the function is finished.
return VALUEreturn myVariableCode that reports the result of an action. The given value is sent back to the code that called the action.
class NAME
end
class Calculator
end
Creates a class, which can contain variables and actions.

CSTA Standards

This lesson covers the following standards:

  • 3A-AP-17: Decompose problems into smaller components through systematic analysis, using constructs such as procedures, modules, and/or objects.
  • 3A-AP-15: Justify the selection of specific control structures when tradeoffs involve implementation, readability, and program performance, and explain the benefits and drawbacks of choices made.

Explore

As you grow more comfortable with programming, you start to pick up ways to reuse code. So far, you have learned how to shorten repetitive commands by using loops and conditionals using "repeat" and "if" statements. This lets you reuse concepts, but as code increases in complexity, you need to reuse bigger pieces.

You have already seen that you can instruct objects to execute named pieces of code. Unfortunately, the field of computer science never really settled on standardized terms, but common ones include "function", "procedure", "method", or "action". In Quorum, they are called "actions", because evidence in the academic literature suggests this name is reasonable [1]. Regardless of the language, this concept is a core feature of modern programming and you really cannot avoid it.

An action is a chunk of code that has a name. Just like you saw when using objects before, you can run the code in an action by calling its name. Actions are essential, especially as your programs start to become more complicated.

Here are a few of the benefits of using actions:

  1. Actions allow you to reuse code.
  2. Actions can abstract code, letting you focus on what task an action performs without worrying about how it does it.
  3. Actions can break up a program into smaller pieces, which can make it easier to divide responsibilities between different people on a team.
  4. Actions can allow others to tap into functionality that they might not know how to write themselves.

Actions are very important. So important, in fact, that you have been using them in every program you have written so far, even if it was not obvious. When you write a Quorum program that does not include any actions, one is secretly generated behind the scenes for you. This secret action is called "Main", and it wraps around all the code you have written. This action is important because a program always starts at the beginning of the special "Main" action, even if you have other actions.

The image below shows a simple Quorum program, and what it secretly converts to under the hood. These two programs are functionally identical.

This is a diagram showing how an output hello style block is secretly wrapped by an action behind the scenes. Visually, it is a flowchart with the output going to an action main wrapping the action. The visual is not a metaphor. Quorum literally does this behind the scenes.

Although evidence in the literature is not definitive, it is at least plausible that Quorum's automatic hiding of this feature is beneficial for some [2]. Once you start to write more complex programs, though, you will need to identify where you want to start your program. This requires you to define your Main function, which you can do with the following lines of code:

action Main 
end

Any program that includes actions must have a "Main" action. A sample program that includes a Main and another action would be the following:

A snippet of Quorum block code with 6 lines that read:
action Main
    SayGreeting()
end
action SayGreeting()
    output ''Hello Friend!''
end

In this code example, the program starts at the Main action and runs each command in order until the program reaches the Main action's end. Look at how the program executes, step by step:

  1. The program always begins on the first line inside the Main action. In this case, it starts on line 2.
  2. The SayGreeting action is called. The program jumps to the first line inside the SayGreeting action, on line 5.
  3. The code on line 5 executes. The program outputs "Hello Friend!"
  4. The program advances to line 6. Because this is the end of the action, the program returns to where it was before the action was called, back to line 2.
  5. The program advances to line 3. Since this is the end of the "Main" action, there is no more code left to run, so the program terminates.

Actions and the Block Palette

In the block palette, the "Actions" tab has several different action blocks available. While they all function similarly, some of the action blocks have additional properties.

A screenshot of part of the Action tab. It shows the three types of actions, as well as the return block.

Actions without Additional Properties

The first block in the palette is the simplest form of action. This is the kind of action you saw in the previous examples so far. It does not have parentheses, extra values (called parameters), or a "return" label. The "Main" action must always be in this format. It cannot take additional parameters.

Actions with Parameters

The second option in the block palette has parentheses, along with a type and a variable name. This action uses what is known as a parameter. Parameters are special variables that are between parentheses in action blocks. These variables only exist in the scope of an action and their value is set to whatever value you use when you call the action.

Parameters provide a way to give input to your actions. This is useful when the action is intended to perform a task on something that could vary. For example, if your action performs math operations, you might need a parameter to specify what value you are performing math on.

The example below shows a program using an action with a parameter. When the code executes, it runs the "PrintValue" action twice. The first time, the provided parameter is 2, so the "value" variable is set to 2 inside of "PrintValue". The second time it is run, the provided parameter is 4, so the "value" variable is set to 4.

A screenshot of a Quorum program, that reads as follows:

action Main
PrintValue(2)
PrintValue(4)
end
action PrintValue(integer value)
output value
end

Actions can have multiple parameters. To do so, you need to separate each parameter with a comma, like so:

action DefaultAction(type name1, type name2, type name3)
    // code
end

Then, calling that action needs the same number of parameters, separated by commas. The order matters -- the first provided parameter always sets the first variable, the second parameter the second variable, and so on. In the example below, the passed parameters are 10 and 6, and the output of the program is 4.

A screenshot of a Quorum program that reads:

action Main
PrintDifference(10, 6)
end
action PrintDifference(integer value1, integer value2)
output value1 - value2
end

Actions with Returns

So far you have created actions that can change their output based on a parameter, but actions can also give back a value. This is where the third block in the action palette comes in. It has a property called a return value." Return values can be thought of as the result of an action. If an action has a return value, you can use the action to set the value of a variable, output to the console, or use the returned value in basically any way that you could use a variable.

The image below shows an example using a return value. On line 2, the action is called, and its return value is assigned to the "result" variable. The return value itself is on line 6, using a special block called "return." The return block indicates what value is the result of the action. Different programming languages do it different ways, but typically return values must have a type and evidence suggests that forcing people to declare a type is, counterintuitively, helpful [3]. In this case, the return type is a number. This means the value in the "return" block must match the type in the action. When the program in the example runs, it gets the result from the action, assigns it to a variable, then outputs it. The output is 7.

A screenshot of a Quorum program. It reads:

action Main
number result = GetLuckyNumber()
output result
end
action GetLuckyNumber returns number
return 7
end

Scope and Classes

In previous lessons, you have learned how multiple line blocks (like repeat and if blocks) have scope, which means that variables declared inside of them cannot be used outside of them. Action blocks also follow this rule, so variables declared inside an action block cannot be used outside of them.

You can also share variables between actions. Since you cannot use a variable declared in one action in another, it would make sense to declare a variable outside of it instead. If you try to do that, though, you will encounter an error. In order to resolve this problem, you will need to use another new block: a class.

An example is shown below, once with blocks and then with code. It shows a program with a class, two actions, and a variable declared outside of the actions. You will learn more about classes later, but for now, you can think of a class as a container that holds actions and variables. For now, all that is important to know about class blocks is that they allow you to share variables across different actions.

The program begins with a variable called "globalVariable" set to 4. When the program runs, it calls the "Double" action, which sets the variable to 8. After the action concludes, the program returns to the Main action and outputs the variable. The output of the program is 8.

This demonstrates a class. The code is

class Main
   integer globalVariable = 4
   action Main
      Double()
      output globalVariable
   end

   action Double
      globalVariable = globalVariable * 2
   end
end
class Main
   integer globalVariable = 4
   action Main
      Double()
      output globalVariable
   end
   action Double
      globalVariable = globalVariable * 2
   end
end
[1] Andreas Stefik and Susanna Siebert. 2013. An Empirical Investigation into Programming Language Syntax. ACM Transactions on Computing Education 13, 4, Article 19 (November 2013), 40 pages.[2] C. L. Corritore and S. Wiedenbeck, "Direction and scope of comprehension-related activities by procedural and object-oriented programmers: an empirical study," Proceedings IWPC 2000. 8th International Workshop on Program Comprehension, Limerick, Ireland, 2000, pp. 139-148, doi: 10.1109/WPC.2000.852488.[3] Stefan Endrikat, Stefan Hanenberg, Romain Robbes, and Andreas Stefik. 2014. How do API documentation and static typing affect API usability?. In Proceedings of the 36th International Conference on Software Engineering (ICSE 2014). ACM, New York, NY, USA, 632-642.

Engage

Now that you have learned the basics of actions, you will make a program that uses them. You will use actions to solve puzzles. While perhaps a bit cheesy, you may learn some magic along the way. Or, well, at least something magic adjacent.

A cluttered table with candles, potion flasks, and a leatherbound tome. The window shows a snowy scene outside. This looks like a wizard's study.
Photo by Amandine BATAILLE

Directions

Welcome to Bogmort's School of Mathcraft and Calculatry. As a new, uh, wizard, you are given four spells that can reshape the very fabric of reality (by performing simple arithmetic on a number). These are the four spells you learn:

  • Incremento: Add 1 to a value.
  • Subtractio: Subtract 3 from a value.
  • Multifour: Multiply a value by 4.
  • Dividenadd: Divide a value by 2, then add 10.

You will undergo four different trials at the school. At the beginning of each trial, you start with 0 magic points. You must use your new spells to increase your magic points to exactly the required number for each trial. For example:

Example Trial: Get your magic points to 9.

You could solve the trial using these spells:

  1. You start the trial with 0 magic points.
  2. You cast Incremento, increasing your magic points to 1.
  3. You cast Multifour, increasing your points to 4.
  4. You cast Dividenadd. Your points are halved to 2, then you add 10, so you have 12 points.
  5. You cast Subtractio, bringing your points down to 9.

To complete the trials:

  • Make a program with integer variables to track your magic points.
  • Create an action for each of your four spells. You may want to create actions that take in parameters and return values in order for your program to work.
  • Because it is proper wizard etiquette to announce your spells when you cast them, you should say or output the spell's name as part of each action (exclamation points optional, but encouraged).
  • In your Main action, use your spells to complete the trials. Output your magic points at the end of each trial to see your result.

Trial 1: Get your magic points to 13.

Trial 2: Using each spell exactly once, get your magic points to 38.

Trial 3: Using only one spell as many times as you need, get your magic points to 963.

Trial 4: Get your magic points to 61 by casting 5 spells or fewer.

Wrap up

Consider what your program would have looked like if you did not use actions. Did having actions make this program easier or harder to write? What if these so-called spells actually did some very complex math instead (e.g., calculate a sine, run a statistical test), would they then be more useful?

Next Tutorial

In the next tutorial, we will discuss Boo Boo Management Part 2, which describes how to use the debugger in Quorum.