VERSION 1 CHAPTER 6 In this chapter we cover ACTIONS in more depth and show how to easily create additional actions in a script by using a technique known as REFACTORING. The chapter covers two forms of refactoring supported in TouchDevelop. One form provides an easy way to create new actions, while the other form provides an easy way to add declarations for new variables to a script. We also show an extension to the second form of refactoring which promotes a local variable to become a new global data item. 6.1 ACTIONS 6.2 PARAMETER PASSING MECHANISMS 6.3 METHODS IN THE API 6.4 REFACTORING 6.1 ACTIONS An ACTION in TouchDevelop is very similar to a function in C or Python, or to a static method in Java or C#. It is an independent sequence of statements which has a name, it optionally has input parameters, and it optionally has result parameters. Let us take a look at the short example in Figure 6.1. This is similar to how the code would appear on the phone s screen. Figure 6.1: The send invite Action action send invite( recips, msg ) returns n n := 0 for each s in recips where true do social send email(s, party invitation, msg) n := n + 1 This send invite action has some properties which can be viewed by tapping the first line of the action, tapping the edit button, and swiping left-to-right. The PARAMS property will show the names 61
VERSION 1 and the datatypes of all the input parameters. For the send invite action, the listing of the parameters is as shown in Figure 6.2. Figure 6.2: The send invite PARAMS Property Similarly, the RETURNS property provides more information about the result returned by the action. It is shown in Figure 6.3. Figure 6.3: The send invite RETURNS Property The input parameters work in a similar manner to parameters of methods in Java or C#. In other words, the parameters are considered to be local variables of the action and, each time the action is invoked, the parameters are initialized by copying values supplied by the caller. Suppose, for example, that one of the other actions in our script is bulk mailer as shown in Figure 6.4. This action creates a String Collection value as the value of variable addr list and initializes the variable msg to hold a String value. When send invite is invoked, its first parameter recips is initialized so that it refers to the same String Collection as addr list. Similarly, the second parameter msg is initialized to hold the same string as the caller s local variable named msg. Now that the parameters of send invite have been initialized, the body of the send invite action is executed. We will not go into any detail about what this action does (suffice to say it will likely annoy everyone whose name begins with j in the contacts collection held on your phone if you actually run it). However, while the action is running, it is computing a value held in the variable n. This variable is declared as an output parameter, but while the action is executing, it should be considered to be just like any local variable. When the action s body has finished executing, the value of this local variable is returned as the result. Its value will be assigned to variable cnt in the caller. 62
VERSION 1 Figure 6.4: A Caller of the send invite Action action bulk mailer( ) var my j friends := social search contacts("j") var addr list := collections create string collection for each c in my j friends where true do var addr := c email address addr list add(addr) // a more realistic example would assign several lines // of text to the msg variable var msg := "Will you come to my party on Friday? " var cnt := send invite(addr list, msg) (cnt " friends have been invited") post to wall Unlike many programming languages, there is no return statement in TouchDevelop. Execution must reach the end of the action before control returns to the caller and any results are returned. Now for some details which serious script developers need to know. 6.2 PARAMETER PASSING MECHANISMS This is a topic which has been partially covered in Chapter 3 already. To recap, there are two kinds of datatypes in the TouchDevelop scripting language. There are VALUE TYPES, where the variable holds the value directly. For example, a variable of type Number corresponds to a word in the memory of the phone which holds the number directly (as a bit pattern). And there are REFERENCE TYPES. Every collection type in TouchDevelop is an example of a reference type. Suppose, for example, a variable has the Link Collection type. The link collection value is a data structure located somewhere in memory and the starting point for that data structure has a memory address (or a location number). A variable in the script which has the type Link Collection would correspond to a word in memory which holds the memory address of the data structure. When a variable which has a reference type is assigned to another variable or is passed as a parameter to an action, we have two references to the same data structure in memory. This situation is diagrammed in Figure 6.5 where the variable num1 has the Number type, while both links1 and links2 are variables with the Link Collection type. After creating a link collection as a list of three e-mail addresses and assigning it to links1, another assignment var links2 := links1 has been executed. Now any changes to the links1 link collection will change the links2 collection and vice versa, because they are both references to the same collection. 63
VERSION 1 Figure 6.5: Value Types and Reference Types Exactly the same behavior is observed for passing parameters to actions. If the parameter has one of the value types then the action has a completely independent copy. If the parameter has one of the reference types, then the caller and the action share references to the same value held in the phone s memory. Which are the value types exactly? They are Number, Boolean, DateTime and Vector3. Every other type is a reference type. RETURNING RESULTS The TouchDevelop editor allows an action to have any number of return parameters. What does this mean? It simply means that the action can return several results at once and they can have different types. As a highly contrived example, consider the action shown in Figure 6.6. Figure 6.6: An Action with Two Results action random stuff( ) returns song, pic var all songs := media songs var all pics := media pictures song := all songs random pic := all pics random 64
VERSION 1 All that matters for our example is that it has two result parameters, one of type Song and the other of type Picture. When it is invoked, the caller can assign the result to two variables simultaneously. Here is an example of how to write the calling code: var s, p := random stuff( ) s play p post to wall It declares s and p as local variables with type Song and Picture respectively, and then those variables are assigned the result of invoking the random stuff action. Not many programming languages support functions or methods which return multiple results. (Programmers in such languages must use various workarounds or less readable programming constructs.) This feature of TouchDevelop can be used to make scripts shorter and more readable. TouchDevelop treats a result parameter as a local variable with an invalid initial value. The action must assign a value to the result parameter before the action returns to the caller. To enforce that requirement, TouchDevelop analyzes the code of the action and checks that at least one assignment happens on every possible execution path through the action. 6.3 METHODS IN THE API The API provides various globals which have methods that can be invoked by a TouchDevelop script. For example, one such global is languages and one of its methods is detect language. (A Java or C# programmer might think of languages as being a class type and detect language as being a static method of the class.) The API also provides a number of datatypes, and values possessing those datatypes have associated methods. For example, chapter 2 used the Picture datatype in an example and it created a local variable named pic with that type. One of the examples used the set pixel method on that pic variable. (A Java or C# programmer might consider pic to be a reference to a class instance and set pixel to be an instance method.) When we write code to invoke these methods, parameter values may be passed into the methods and results may be returned from them. Although the API s methods are all implemented in the C# language (as is the whole TouchDevelop application), the methods all behave similarly to Touch- Develop actions. Value types are passed in to the methods using the call-by-value mechanism and reference type values are passed in using call-by-reference. Although methods provided by the API have the potential to return multiple results, none does. Every method provided in the API so far either returns a single value or it returns Nothing. 65
VERSION 1 6.4 REFACTORING The word REFACTORING is used in software engineering to mean a reorganization of a program which does not change its behavior. Such a reorganization is normally performed to improve the quality of the software in some way, such as to make the code more efficient or to be more readable by humans. Most modern environments for software development, such as Visual Studio and Eclipse, provide a variety of tools to support different forms of refactoring. The TouchDevelop editor provides two forms of refactoring. One form extracts a group of statements from an action and creates a new action, with appropriate parameters, from those statements. We might want to perform such refactoring if we have an action which has become too long to comfortably display on the screen and we simply want to split the action up into smaller pieces. Or, perhaps we have recognized that there is a particular group of statements which we will need to execute several times with different parameterization. These are both excellent reasons to extract and create a new action. Finally, a TouchDevelop user is likely to find this form of refactoring to be a time saver because fewer taps and few swipes are needed to create a script composed of multiple actions. The second form of refactoring is to extract an expression or a subexpression, replace it with a newly created variable, and insert an initialization of that new variable with the extracted expression. A further step beyond extracting an expression and creating a new local variable is to convert that local variable into a new globally visible data item. This would make the value of the variable accessible to all the actions in the script (as well as preserving the value of the data item from one invocation of the script to the next invocation a feature known as persistence.) REFACTORING: EXTRACTION OF STATEMENTS TO A NEW ACTION Let s go through an example of refactoring in TouchDevelop. Chapter 2 contained an example, the Random circles script, which was constructed and entered as two separate actions. Suppose that we had entered the script as just one action. It would look like the script shown in Figure 6.7. There is nothing wrong with this version of the script, it is not too long and it runs perfectly well. However, we might decide that the group of statements which draws the circle is so useful that we want to extract that code and make a new action from it. 66
VERSION 1 Figure 6.7: A Monolithic Random circles Script action Random circles( ) pic := media create picture(400, 400) for 0 i < 20 do var radius := 5 + math random(95) var x := math random(400) // x coordinate of center var y := math random(400) // y coordinate of center var color := colors random for 0 i1 < 2 * math * radius do var xofs := x + radius * math cos(i1 / radius) var yofs := y + radius * math sin(i1 / radius) pic set pixel(x + xofs, y + yofs, color) pic post to wall Here are the steps we might perform extract the circle drawing code (which corresponds to the inner for loop). 1. Touch anywhere in the first line of the group of statements we wish to extract. For our example, that is the inner for loop (the one which uses i1 as the index variable). The screen should now look like this: 67
VERSION 1 2. Now touch the button labeled select more and the screen changes to look like this: 3. It so happens that everything we want has been selected because it is a single statement (the entire for loop) identified by the red bar on the left and show between the upper and lower rows of buttons. If we wanted to selected additional statements, we could drag the group of statements upward to increase the range of statements to be extracted. In our example, we do not need to do that, so we can proceed to tap the extract button. At this point, we are asked to provide a name for the extracted code (the new action). We enter the desired name, draw circle, and tap another extract button. 4. The screen should change to show the revised Random circles action after the refactoring has been performed. Here is a screenshot: 68
VERSION 1 5. The editor has created an invocation of the new action, with the name we gave it and with whatever parameters were needed to transmit values of variables from the caller (the Random circles action) to the callee (the new draw circle action). At this point, the refactoring has been completed. The modified script should work just as before. Finally, here is a quick way to see the code of the new action (or of any action being invoked in a script). We touch the line of code containing the call to the action. This brings up the edit statement menu, which looks like the following for our example: The go to button will take us to the code for the action invoked in the selected statement. After touching it, the screen will show our refactored action: The code is remarkably similar to the code originally shown in Chapter 2. The only differences are the order in which the parameters appear and the name used for the index variable of the for loop. These would usually be considered to be trivial and unimportant differences. REFACTORING: EXTRACTION OF AN EXPRESSION TO A VARIABLE If you discover that you have constructed an expression whose value you would like to reuse, or if you have a complicated expression which you would like to break up into smaller pieces, this form of refactoring is just what you need. Let s take a small example. Suppose that you have just entered the following script which calculates the length of the diagonal side of a right-angled triangle whose sides are x1 and y2: 69
VERSION 1 Perhaps we are now going to extend this script to perform more calculations and we will need to reuse the values of x1*x1 and x2*x2 in some statements we will add to the bottom. Here are the steps to follow. 1. Tap the line containing the expression you wish to extract. In our sample script, that is the line beginning var x := 2. Tap the first element of the expression, which is the first x1 appearing in that line. 3. Swipe from left to right, to highlight all the elements of the selected expression. For our example, the screen should now look like this, with a menu of available editing actions at the bottom: 4. Tap the extract to local button. The statement shown on the screen changes to the following, indicating that a new local variable named x3 has been created. 5. Let s repeat steps 2 to 4 in the same way to extract the expression x2*x2 into a new local variable named x4. 6. We can tap the phone back button to see how the code for our script has been transformed. 70
VERSION 1 7. If we do not like the default names provided for the new local variables, they are easy to rename. One just has to tap any line where the name appears, choose the option to edit the line, select an occurrence of the variable, and there will be a rename button provided on the screen. REFACTORING: PROMOTING A LOCAL VARIABLE TO A GLOBAL DATA ITEM Perhaps we are going to expand the example script by adding many new actions to it and lots of these actions will need access to the values of the new variables x3 and x4. One possibility is to pass x3 and x4 as parameters to the various actions. Another possibility is to convert x3 and x4 into global data items. TouchDevelop provides an easy way to make that transformation. The steps for converting the local variable x3 in the preceding example into a new data item are as follows. 1. Tap the line which declares and initializes the local variable. 2. Tap the edit button. 3. Tap the variable on the left-hand side. An edit menu like that shown below appears. 4. Tap the promote to button. The code displayed on the screen changes to that shown below, indicating that x3 is now being accessed as a global data item. A return to the main screen for the script would show that the script now includes x3 as a data item. 71
VERSION 1 72