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:
Term | Definition |
---|---|
Action | A named sequence of code that can be reused later in a program. |
Action Call | Code that tells an action to run. |
Parameter | A special variable inside an action. It must be provided as an input when the action is called. |
Return Value | The result of an action, which can be used like a variable. |
Scope | The area where a variable exists and can be used. |
Class | A structure that defines a custom Object type. It can contain variables and actions. |
Code
You will be using the following new pieces of code:
Quorum Code | Code Example | Explanation |
---|---|---|
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 VALUE | return myVariable | Code 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:
- Actions allow you to reuse code.
- Actions can abstract code, letting you focus on what task an action performs without worrying about how it does it.
- Actions can break up a program into smaller pieces, which can make it easier to divide responsibilities between different people on a team.
- 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.](/media/Blocks/ActionsClasses1/SecretMainAction.png)
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](/media/Blocks/ActionsClasses1/ExampleMainAction.png)
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:
- The program always begins on the first line inside the Main action. In this case, it starts on line 2.
- The SayGreeting action is called. The program jumps to the first line inside the SayGreeting action, on line 5.
- The code on line 5 executes. The program outputs "Hello Friend!"
- 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.
- 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.](/media/Blocks/ActionsClasses1/ActionPalette.png)
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](/media/Blocks/ActionsClasses1/PrintValueExample.png)
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](/media/Blocks/ActionsClasses1/PrintDifferenceExample.png)
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](/media/Blocks/ActionsClasses1/ReturnExample.png)
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](/media/Blocks/ActionsClasses1/ClassExample.png)
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.](/media/Blocks/ActionsClasses1/WizardTable.png)
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:
You could solve the trial using these spells:
- You start the trial with 0 magic points.
- You cast
Incremento , increasing your magic points to 1. - You cast
Multifour , increasing your points to 4. - You cast
Dividenadd . Your points are halved to 2, then you add 10, so you have 12 points. - 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.