Beyond The Blocks
Custom Reporter Blocks
Create Functions That Return Values to MakeCode
by David Sparks
Originated September 2019
This article will explain how to add custom blocks that execute a function and return a value to a MakeCode project.
Functions that return values become "reporter" blocks in MakeCode. They are shaped to fit the openings in the puzzle-piece blocks. You can use them like variable names. It will be easiest to introduce reporter blocks with the example in Figure 1.
Figure 1
A custom reporter block
How did we get this custom block? By writing specially-formatted code.
I made some assumptions about the knowledge level and the goals of people most likely to read this article. I imagine you:
- have created some functions using the standard blocks in MakeCode.
- are willing and able to look things up in references and to learn by trial and error.
- are familiar with writing code as text and with using functions in a programming language.
- wonder where the "return" statement is because you would like to write a MakeCode function that will return a value.
Readers having some experience with coding may get the most value from this article. However, every reader is welcome and I hope this article will at least help you see that MakeCode can take you far beyond the standard blocks.
The topic of Functions is far too large to teach or to explain in a short article like this. The W3Schools web site offers a good introduction at the following link: JavaScript Functions.
The introductory articles in this series illustrated in detail the steps to create custom blocks using the MakeCode editor. Please refer to the articles listed below if you have a question about the steps. "Top Ten Tricks" also lists the references that I go to often when I need to look something up.
- Top Ten Secret Tricks: How to Create Custom Blocks in MakeCode
- Custom Constant Blocks for MakeCode: An Illustrated Tutorial
Open the custom.ts file in a new project. Quick reminder — click links in the MakeCode editor in the sequence listed below to open the custom.ts file in a new project.
- JavaScript button
- Explorer button
- plus sign ("+")
- "Go ahead!" button
Replace the default code in custom.ts with the listing in Exhibit 1, below. The syntax is important and this example will be discussed in detail later in the article. For now, it might be best simply to copy the code and paste it into the file, replacing what was there.
Exhibit 1
Code for a custom reporter block
/**
* Custom blocks
*/
//% weight=100 color=#0fbc11 icon="\uf0c3"
namespace custom {
/**
* convert a temperature
* given in degrees Celsius (°C)
* into its equivalent
* in degrees Farhenheit (°F)
*/
//% block="fahrenheit|%celsius"
export function fahrenheit(celsius: number): number {
return (celsius * 1.8) + 32;
}
}
Click the Blocks button to compile the code. Then refresh the browser to activate the new block. It will be in the "Custom" group that appears in the main blocks list. Use the block to convert negative 13.3333 degrees (below zero) Celsius into its equivalent: 8 degrees (above zero) Fahrenheit. (Figure 2)
Figure 2
Showing the Custom group and the fahrenheit reporter block being used.
Reporter blocks are oval-shaped for numbers and strings. For bookean values they are six-sided with pointed ends, like the true and false blocks in the Logic group.
Their shape gives a clue how to use them. They fit into openings in the stackable blocks that look like puzzle pieces.
MakeCode responds to the values returned by reporter blocks as if it were reading the value of a variable.
Exercise #1
Look through the standard blocks. Count the reporter blocks you find. Each one has code similar to Exhibit 1. Now you have seen how create your own.
The Syntax of a Reporter Block
Every line of code listed in Exhibit 1 can teach something about writing custom reporter blocks.
The Comments
MakeCode examines the contents of comments. Sometimes it uses information from the comments when it builds a custom block from your code.
This feature of the MakeCode editor is important to understand for writing custom blocks. We will explain in the sections that follow.
It remains a good coding practice to include regular comments in your code.
A quick review of JavaScript comment syntax
Regular comments provide helpful documentation for humans to read but generally do not participate in the execution of your code at run-time.
JavaScript offers two ways to encode comments.
- Multi-line comments begin with the character pair,
/*
and end with the mirror-image pair,*/
. All the lines between these two pairs will be included in the comment. As a visual clue to humans reading the code, it is helpful to place an asterisk at the start of the lines in-between, and to align the initial asterisks vertically. The following segment from Exhibit 1 is a multi-line comment./** * Custom blocks */
- A single-line comment can be wrapped with the multi-line pairs,
/* like this */
, or it can begin with a pair of slant characters,// like this
.
The Custom Group Metacode
MakeCode extends the idea of the single-line comment by adding a %
character to define a special kind of comment, called metacode. These special comments give instructions for how to prepare your code.
The distinction between regular comments and metacode comments deserves some emphasis.
- Regular comments have no effect on how your code, neither how it compiles nor how it runs.
- Metacode comments convey actual instructions to MakeCode. They affect how your code compiles, but not how it runs.
Metacode comments begin with //%
, followed by definitions. A definition is a name=value pair.
The Custom group metacode precedes the namespace (discussed next) and tells MakeCode how to set up the Custom group.
//% weight=100 color=#0fbc11 icon="\uf0c3"
A metacode comment line can contain more than one instruction. This example has three:
weight=100
affects where the Custom group will be placed in the list of available groups of blocks.color=#0fbc11
specifies the color, in rgb format, of the Custom group and any blocks it will contain.icon="\uf0c3"
specifies the flask-shaped icon associated with the Custom group.
The Namespace
Custom blocks must be coded inside a JavaScript namespace. The following lines selected from Exhibit 1 declare and enclose the namespace.
Notice the curly brackets that must be present both before and after the actual custom block code.
namespace custom {
// your code goes here
// between the curly brackets
}
The Pre-Block Comment
Make a habit of providing a pre-block comment for each of your custom blocks. The format resembles a JavaScript multi-line comment, with some special detail.
The first line will have a slant followed by two asterisks: /**
.
Lines that begin with an asterisk followed by plain text will become the "help" text for your custom block. You can see this illustrated in Figure 1, above.
Use tabs and space characters to line up the asterisks carefully. Follow the pattern presented in the listing below, and in Exhibit 1.
The final line will have an asterisk followed by a slant:*/
Here is the complete listing for the pre-block comment of the fahrenheit block:
/**
* convert a temperature
* given in degrees Celsius (°C)
* into its equivalent
* in degrees Farhenheit (°F)
*/
The Essential //% block
Metacode
The official documentation for custom blocks states, "Any exported JavaScript function can be turned into a block by simply adding a //% block comment."
In other words, you must precede the code for your block with this metacode:
//% block
It tells MakeCode to prepare the code that follows as a block.
The //% block
metacode is an exception to the usual name=value format for other metacodes. //% block
does not require a value.
If no value is supplied, MakeCode will by default use the name of the function as the name of the custom block. However, if you want to include a parameter in the block then you need to supply a formatting string as the value for the //% block
metacode. The string determines the visual presentation of the custom block.
The fahrenheit block in our example incorporates a parameter. It must tell MakeCode where to place the parameter in the block. The fahrenheit block's formatting string places the parameter at the end:
//% block="fahrenheit|%celsius"
The formatting string specifies:
- the name of the block, "fahrenheit"
- a vertical bar, indicating where to place a space character
- a
%
character, indicating where to place a parameter - the name of the parameter to include in the block (leaving no space between the % character and the parameter name)
Parameters can be placed between words in a //% block formatting string. An example of this will be provided later in the article.
Declare and Export the Function
As stated by the documentation quoted above, your function must be exported to become a custom block. What does it mean?
Simply place the keyword, export
, at the start of the line that declares the function.
export function fahrenheit(celsius: number): number {
There is a lot going on in this declaration:
- The function receives a name. JavaScript will access it by the declared name, not by the name given in the
//% block
comment.
The two names can be different, though in this example I chose to keep them the same. - The parameter is declared to be a specific type: number. This is different from JavaScript, which allows values to be of any type.
MakeCode is based on Static TypeScript, an enhanced version of JavaScript that does restrict values to having only one type.
Declaring the celsius parameter to be of type: number, means that it will only accept numbers as arguments. - The function, also, is declared to be of type: number. The type declaration of a function comes after the closing parenthesis
)
of the parameter list, and goes before the opening{
curly bracket of the function's code body
Giving the function a type is a key part of turning it into a reporter block. It promises MakeCode that the value returned (that is, reported) by the function will be of that type.
For example, the fahrenheit funtion must ensure that it returns a number. - Finally, it should go without saying, the declaration provides the opening curly bracket that precedes the actual function code.
This bracket must be matched by a closing bracket at the end of the function.
The return
Statement
A reporter block must include a return
statement. In this example, it is the only line of code in the function. The statement performs the conversion calculation and returns the result.
return (celsius * 1.8) + 32;
Semicolons!
The code you write for custom blocks must conform to JavaScript syntax. This means that semicolons are required at the ends of statements.
This behavior is more strict compared to the way MakeCode deals with the JavaScript for the main code of the editor, where semicolons are optional.
A bit of deliberate trial and error with semicolons will help you come to grips with the nature of coding custom blocks.
Frequently-asked Questions
What types of values can be returned by a reporter block?
Custom blocks like our fahrenheit example can return any one of the so-called primitive types: numbers, strings, and boolean (true/false) values.
Simply declare the function to be of the corresponding type.
More advanced types of JavaScript values can be returned also, including arrays and objects. These types are outside the scope of this article. I intend to cover them elsewhere in this series.
What else can I do with custom reporter blocks?
Functions can do many things. Study examples provided in the documentation. Use your imagination. Try things; find out what works. A partial list might include:
- Return a constant value. See the examples in another article of this series: Custom Constant Blocks
- Expose a JavaScript function that got left out of the standard blocks. For example:
//% block="log10|%arg" export function log10(arg: number): number { // report the base-10 logarithm of arg return Math.log(10) * Math.LOG10E; }
- Perform a calculation based on a parameter and return its result. For example, the following code creates a block that takes a number of dice as a parameter and returns the sum of rolling that number of six-sided dice by simulation.
Notice that the formatting string places the count parameter between the words. Also, there is a new metacode in this example://% count.defl=2
. It establishes a default value for the count parameter. (Figure 3)/** * simulate rolling a given number of * six-sided dice and return the total */ //% block="roll|%count|dice" //% count.defl=2 export function rollDice(count: number): number { // initialize total let total = 0; // simulate rolling some dice for (let x = 0; x < count; x++) { total += Math.randomRange(1,6); } return total; }
- Obtain and process a value from a sensor attached to the micro:bit. For example, modify the fahrenheit block so that the code inside the function obtains the value of the micro:bit's temperature sensor and converts it to fahrenheit.
The block would not need a parameter because it directly fetches the value it needs. Hint: the JavaScript function you will need, "temperature", is located in the input group of built-in blocks.
I leave it as an exercise for the reader. - I intend to build a collection of reporter-block examples in the companion repository for this article, located in the IowaDave area of GitHub.
Figure 3
Showing the rollDice custom block in use with a result in the micro:bit display
Where can I learn more about defining functions and parameters for reporter blocks?
There are many metacode instructions for custom blocks. Extensive information, with examples, is provided at the following link: https://makecode.com/defining-blocks.
The author especially appreciates all of the "playground" examples this resource provides. Very, very helpful.