After addressing arrays, scales and rings, I was looking to have the students do a project that would show their understanding of how to use them since it had been a while since I gave them a graded assessment. But I wanted it to be more than just a sequence of these commands without any real form to it. I felt like I needed to have it joined up with some other concept to give it more structure. My first thought was to have them learn in_thread to be able to join two threads together and have them create bass lines or drones to play underneath some scales or melodies they made with arrays and rings. But as I played around with how I could do that, it just felt like it was too complicated a concept to roll out in one class and then hit them with a project with expectations of them using it correctly in addition to the other concepts we had covered.
The next thought I had was functions. This was definitely a concept I thought the students could grasp quickly and it would be a good format to include the data structures we had been talking about. I figured I could spin this into another form project but using functions for each section instead of the conditional statements we had done for the last form project. It would also fit the class structure I have been trying to stick to which is around a 15 minute time limit on presenting a concept and allowing the rest of the class for exploration. And finally, it allowed students to come up with their own names for things which they really seem to get a kick out of.
So I made up my mind to go with functions. When I presented it to the students, I figured it made the most sense to introduce it as part of our recent theme of making our code more concise as we had already been doing with arrays and rings. I’ve been saying that since we now have a general working knowledge of how this language works, it always optimal to keep our code short and effective, so functions fit write in with that. I told them that functions are basically a series of instructions to be carried out by our program. We start by giving this set of instructions a name, which we refer to as defining the function. We use the command define , assign a name and put the code in a do/end block. The example I made I called bob, just to show it could really be anything.
define :bob do 16.times do play scale(:e4, :major).choose sleep 0.5 end
Next, I ran the code to make the point as to how functions work. I pointed out to the students that we did not get any error message, however we also did not hear any sound. What has happened is that I have only defined the function. By doing this, Sonic Pi now knows that whenever it sees the word bob, it is to run the code indicated in the define block I just wrote. However, to do this, we need to do what is referred to as “calling the function” by writing the name of the function below. I wrote the name of the function, bob, and ran the code at which point we heard the code.
I was very deliberate in how I phrased this part of the explanation. I could have easily said “this is what is called “calling the function” but I felt like using the word call twice would have been confusing. I have also tried to do that with any use_ commands like use_synth or use_bpm. I always say “include use_synth”, not “use use_synth”. While this may seem nit-picky, my thought is that learning how to program is like learning a language and understanding how to use syntax is difficult enough without adding confusion when explaining them. Just the sound of hearing those words twice in a row could throw off students, especially those with some type of learning disability. So any accommodation I can offer in regards to making it less confusing, I will try to do.
After running bob once, I then wrote bob a second time to show that I can now reuse this code easily without copying multiple lines of code. I then put a use_synth before the second bob to show that these types of commands still work on the code inside the function, even though it happens after. I also did that just to make it more obvious that the same function was running twice, which may not have been clear the first time I ran it twice. To further illustrate that point, I put two different use_random_seed commands before each bob function, to show how I could change the pattern since I had used .choose for my scale.
define :bob do 16.times do play scale(:e4, :major).choose sleep 0.5 end use_random_seed 123 bob use_synth :chiplead use_random_seed 654 bob
From there, I said we are not limited to one function. I could write another, or two more or twenty more if I wanted to. So I started my next function. Before I could decide on what to call it, kids were shouting out names. “Call it ‘Earl’” stood out to me, so I went with that and named my next function earl. At this point, I made it clear that we can name our functions anything except names of commands that are already part of Sonic Pi, like play or sleep because Sonic Pi will see those words and already be expecting something else to happen. (Later the kids found out that Sonic PI also does not like functions that begin with capital letters!)
I wrote the earl function similar to bob, this time using another scale and a ring for my sleep values. I wanted to reinforce the use of data structures since that was my initial objective of why I was teaching them functions. I also added a synth sound to earl to make sure the students could hear the difference when that function started.
define :earl do use_synth :chiplead 8.times do play scale(:c3, :bartok).choose sleep (ring, 0.25, 1, 0.25, 0.5).tick end end
After running both of those functions in the order bob then earl. I reversed the order and ran them again. I drew attention to the fact that when bob came first, it had the regular beep sound, but now it has taken on the synth sound from earl since I never gave bob its own synth sound. I wanted to make the students aware that functions will take on setting from whatever came before them if that command is not included in the function. To prove that, I then added the use_synth :beep to the bob function and after earl played, bob went back to the regular beep sound. My working knowledge of CS terms is not very extensive but I really felt like this was an example of inheritance. However, since I wasn’t sure, I just avoided using that term for the students. If anyone reading would like to confirm that one way or the other, please feel free to comment.
At this point, I was done explaining functions and turned the class loose to explore. Most of the issues I came across were students forgetting to call the function after defining it. Beyond that, it seemed like a relatively easy concept for them to grasp and incorporate what they already know into that structure.
As I have been doing in the last few classes, students were expected to submit their code via Google Classroom so I could check on how they did with the concept. While I have really been liking this accountability, there hasn’t been any type of grade or follow up with any comments I might leave, so some students haven’t been going back to check the code or even submitting code in some cases. As I move forward, I plan on coming up with something to see to it that all students are submitting code and reading comments, so that they aren’t making mistakes and not realizing it or so students aren’t just blowing off submitting at all because it doesn’t appear to affect their grade in anyway. If I do this class again next year, this will definitely be built into the course from the beginning.
Next week, I will assign them the form project using functions.