ToDo: Make ECE2524 Obsolete

Why would I want to eliminate the course that I’ve been teaching the past four semesters, that I have put so many hours into to update content, create new assignments, written (and re-written each semester… another topic altogether) a set of scripts to facilitate reviewing stacks of programming assignments and generally had a great time with?

Well, because I don’t think it should be a separate course to begin with. As many have noted, and I have agreed, ECE2524 in many respects is a kind of a “catch-all” course for all those really important topics and tools (version control, anyone?) that just don’t get covered anywhere else. It is also officially (but not rigorously enforced in the form of a prereq) to be an introduction to more advanced software engineering courses, so it has the general feel of a programming course.

I think programming (and *nix OS usage and philosophy) is too important to delegate off to a 2 credit course and treat separately from the rest of the engineering curriculum, an idea that was solidified after reading an excerpt from Mindstorms by Seymour Papert.

I began to see how children who had learned to program computers could use very concrete computer models to think about thinking and to learn about learning and in doing so, enhance their powers as psychologists and as epistemologists.

Papert is a strong advocate to introducing computer programming to children at an early age and using it as a tool to learn other disciplines

The metaphor of computer as mathematics-speaking entity puts the learner in a qualitatively new kind of relationship to an important domain of knowledge. Even the best of educational television is limited to offering quantitative improvements in the kinds of learning that existed without it… By contrast, when a child learns to program, the process of learning is transformed. It becomes more active and self-directed. In particular, the knowledge is acquired for a recognizable personal purpose.

It goes without saying that a solid understanding of math is crucial for any of the STEM fields, but computers and programming can also encourage engagement with other fields as well, though that is not the focus of this post.

Along with being a useful skill to have, programming teaches a systematic way of thinking about a problem, and crucially shifts the model of learning from one that embodies a “got it” and “got it wrong” binary state to one that encourages the question “how do I fix it?”. As Papert notes, and I can personally attest, when writing a program you never get it right the first time. Becoming a good programmer means becoming an expert at tracking down and fixing bugs.

If this way of looking at intellectual products were generalized to how the larger culture thinks about knowledge and its acquisition, we all might be less intimidated by our fears of “being wrong.”

Some strong arguments for the symbiosis of programming and learning valuable thinking skills at an early age. But the benefits don’t disappear at the college level, especially in a field such as engineering in which learning programming for the sake of programming is a valuable skill (there are several required classes on the subject, so you know it must be important. Slight sarcasm, but it’s true, regardless of how cynical we agree to be about the way classes are structured and the curriculum is built for us). If programming can help engage with learning mathematics, and as a side effect get us thinking about how we think, and shift our view of learning to a more constructive one, then can’t we get at least the same positive affects if we apply it to more advanced concepts and ideas? It doesn’t hurt that a good chunk of engineering is mathematics anyway.

The wheels really started turning after the first day of guest-lecturing for Signals & Systems. Here’s a course that is a lot of math, but critically foundational for learning how to learn about how the world works. That may seem a little embellished, especially for those not familiar with the field (Signals & Systems crash course: a system is anything that has an input signal and produces and output signal, e.g. a car (input is gas/break, output is speed), a heart beat (input is electrical signal transmitted along nerves, output is muscle contraction or blood flow), the planet (so many systems, but treating atmospheric concentrations of CO2 and other gases as an input and the average global temperature would be one example of a system we would be interested in studying)). Signals & Systems provides a set of tools for exploring the input/output relationships of… anything.

So why is it taught from a set of slides?

What better way to really engage with and understand the theory than USE it? Now most educational budgets wouldn’t be able to cover the costs if everyone wanted to learn the input/output behavior of their own personal communications satellite, but the beauty of Signals & Systems, and the mathematical representations that it embodies, is that everything can be simulated on a computer. From the velocity of a car, to blood flow caused by a beating heart, to the motion of the planets and beyond.

I envision a Signals & Systems course that is mostly programming. People will argue that the programming aspect of the material is just the “practical implementation”, and while that’s important, the theory is critical. Yes, the theory is what helps us develop a generalized insight into different ways of representing different types of systems and what allows us to do a good deal of design in a simulated environment with greatly reduced risks, especially when say, designing new flight controls for a commercial jet.

But I think the theory can be taught alongside the programming for a much richer experience than is obtained by following a set of slides. You want to understand how the Laplace transform works? What better way than to implement it on a computer. I guarantee you, if you have to write a program that calculates the Laplace transform for an arbitrary input signal, by the time you’re done with the debugging, you’re going to have a pretty good understanding of whats going on, not to mention a slew of other important experiences (how do you solve an integral on a computer anyway?).

Talking about the differences between continuous time systems and discrete time systems is taken to a whole new level when you start trying to simulate a continues-time system on a computer, very much a discrete-time system. How do you even do that? Is it sufficient to just use a really really small time step?

So yes, I think the best case scenario would be one in which ECE2524: Intro to Unix for Engineers is obsolete1. Not because the topics we cover are unimportant, quite the contrary, they are so important that they should be providing a framework for learning engineering.

Footnotes:

1 I’ve focused primarily on the programming aspect of ECE2524 here, but those of you who know me and have taken the course with me know that the Unix philosophy is a big part of it as well. Integrating the programming aspects into other coursework would of course not address that. I’m sure, with a little thought we all can think up a clever way of introducing the *nix philosophy and generally the whole concept of thinking about a philosophy when thinking about engineering, and what that even means, with every other course. Because well, it should be an integral part of everything else we learn.

Are we sacrificing creativity for content?

I decided to become an engineer, before even knowing what “engineering” was, because of a comment my 4th grade art teacher made regarding an art project. I’m pretty sure she meant it as a complement.

The concept of “<insert form of creative expression here> is <insert sensory-related word here> math” is nothing new. From the mathematics of music, to the use of perspective in visual art, there is no escaping the mathematical nature of the universe. All art, no matter the medium, can be thought of as offering a different view of our underlying reality. A different way of looking at the equations, a way at looking at math without even realizing it’s math.

Then why in the engineering curriculum is the emphasis all on the math? Sure, it’s important. Knowing the math can mean the difference between a bridge that collapses1 and one that is a functional art exhibit. Or the difference between a Mars Climate Orbiter that doesn’t orbit and a Mars rover that far exceeds its planned longevity. But it’s still just one view.

If you have ever tried applying the same layering techniques using water colors that are commonly done with oil paints, or tried to write a formal cover letter in iambic tetrameter, you have first hand experience that the choice of the medium has a large impact on the styles and expressive techniques available to the artist. Likewise, the choice of programming language has a similar affect on the capabilities and limitations of the programmer.

see the code

And on the flip side, anyone who can write a formal cover letter, or who is intrigued by writing one in iambic tetrameter, should learn a programming language or two. It’s yet another form of artistic expression, one that can transform the metamedia of the computer into a rich, expressive statement, or produce an epic failure of both form and function.

Footnotes:

1 Though there is a beauty to the mathematics of this particular failure.

I am a Selfish Git: A bit on my teaching philosophy

A common observation I encounter from people who have taken my class is that there is less structure in the assignments than they are used to, and oftentimes less than they would like. A consequence of this is that participants do a lot of searching the web for tidbits on syntax and program idioms for the language du jour, a process that can take time with the wealth of information that is returned by a simple google search. I could track down some research that shows the benefit of this “look it up yourself” approach, and it would all be valid and it is one of the reasons I structure assignments the way I do, but there is another reason. A more selfish reason.

Throughout the term I’ll assign a series of assignments. Details are tweaked each semester but the general outline is something like:

  • read in lines of numbers, one per line, do something with them and write out a result number.
  • read in lines of structured data, do something with them, write out lines of structured data
  • spawn a child process, or two, connect them with a pipe (this year I will probably integrate the “read in lines” idiom into this assignment since I like it so much)

I’ve done each of these myself of course, and tweaked my own solutions from year to year and have found a structure for each that I think works well, is easy to read and is as simple as possible. Often times my solutions use fewer lines of code than some of the solutions I receive, which admittedly make my estimates of how long a particular assignment will be inaccurate. I know some of the assignments end up taking a lot longer than I anticipate for some, and this can be
extremely frustrating, especially since I know everyone’s time is a precious commodity that must be partitioned across other classes and personal time too (you are making time for play, aren’t you?).

I could provide more details in the write-ups. I could say “I tried algorithm X a number of ways: A, B and C, and settled on B because P, Q and R”. It would save those completing the assignments time and it would save me time, because on average the results I’d get back for grading would take up fewer lines of code and be more familiar to me. And that is why I don’t.

If I wrote in the assignment and said “for part A, use method B in conjunction with idiom X and you can complete this part in 3 lines” then I can guarantee you that around 99% of the 60 assignments I received back used method B in conjunction with idiom X in only 3 lines of code. It would be much easier to evaluate: I’d be familiar with the most common errors when using method B in conjunction with idiom X and would have made spotting them quickly a reflexive response.

But I wouldn’t learn a thing.

Let me tell you a secret. Sure, I enjoy seeing others learn and explore new ideas and get excited when they discover they can write something in 10 lines in Python that took them 30 in C. I really do. But that’s not the only reason I teach. I teach because I learn a tremendous amount from the process myself. In fact, all that tweaking I said I’ve done to my solutions? That was done in response to reviewing a diverse (sometimes very diverse) set of solutions to the same problem. Often times I’ll get solutions written in a way I would never have used solving the problem myself, and my first reaction is something like “why does this even work?” And then I’ll look at it a little closer (often times using a fair amount of googling myself to find other similar examples) until I understand the implementation and form some opinion about it. There are plenty of times that I’ll get a solution handed to me that I think is cleaner, more elegant and simpler than my own, and so I’ll incorporate what I learned into my future solutions (and let’s not forget back into my own work as well, a topic for another post). And I’ll learn something new. And that makes me happy.

I really like learning new things (thank goodness for that, given how long I’ve been in school!), and I have learned so much over the past couple years that I’ve been teaching. Possibly more than what I’ve learned in all the classes I’ve taken during my graduate career (different things for sure, which makes it difficult to compare amount, but still, you get the idea).

To be sure, there is a balance, and part of my own learning process has been to find out that sweat spot between unstructured free-style assignments (“Write a program that does X with input B. Ready go!”) and an enumerated list of steps that will gently guide a traveler from an empty text file to a working program that meets all the specs. I think I’ve been zeroing in on the balance, and the feedback I get from blogs as well as the assignments themselves is really helpful.

So keep writing, and keep a healthy does of skepticism regarding my philosophy. And ask questions!

Structure, Language and Art

In a recent post tylera5 commented that the last time he wrote poetry was in high school, and wasn’t expecting to have to write a poem for a programming course. I got the idea for a poetry assignment from a friend of mine who teaches a biological science course. She found that the challenge of condensing a technical topic into a 17 syllable Haiku really forces one to think critically about the subject and filter through all the information to shake out the key concept. And poems about tech topics are just fun to read!

I think the benefit is even increased for a programming course. As tylera5 mentioned, both poems had a structure, and he had to think a bit about how to put his thoughts into the structure dictated by the poetry form, whether it be the 5/7/5 syllable structure of a Haiku, or the AABBA rhyming scheme of a limerick.

Poetry is the expression of ideas and thoughts through structured language (and the structure can play a larger or lesser roll depending on the poet, and type of poetry). Programming also is the expression of ideas and thoughts through structured language. The domain of ideas is often more restricted (though not necessarily, this article and book could be the subject of a full post in its own right) and adherence to structure is more strict, but there is an art to both forms of expression.

Are there artistic and expressive tools in other STEM topics as well?

Response to “Advanced Python exercises”

Recently Matt brought up some good points in his post Advanced Python exercises.  First, the more easily addressed one:

To break up a Python program into multiple functions, just store related functions in a separate `.py` file, then in the main source use import

For example, if you have a file named `hello.py` that contains

def greeting(string):
    return "Hello, {}".format(string)

Then in your main source file (located in the same directory), you can import `hello.py` and all the functions will be available from the `hello` namespace:

import hello

print hello.greeting("world")

Easy as .py! (sorry)

Also in the post Matt talked about Python’s use of “try” and “except” as a means of flow control, as an example:

try:
    mynumber = float(line)
except ValueError as e:
    sys.stderr.write("We have a problem: {}".format(e))
else:
    print "We have the number {}".format(mynumber)

He said “if complex return types are needed such that you’re throwing exceptions to communicate logic information rather than true fatal errors, your function needs to be redesigned.” which I agree with. But I disagree that Python itself relies on this technique, or encourages it, though of course individual developers may miss-use it.

Like the programs they are a part of functions should be written to do one thing and one thing well. In the preceding example the function float converts its argument to a floating point value. The name of the function makes it very clear what it does, and it does its job well. If it can’t do it’s job, then it raises a ValueError exception.  All other built in and library functions I’ve seen work the same way.  Think about the alternative without exceptions, for example, C:

double n = atof(line);
printf ( "We have the number %f\n" , n);
//Except if n == 0 it could be because there was no valid numeric expression in line

According to the documentation for atof:

On success, the function returns the converted floating point number as a double value.
If no valid conversion could be performed, the function returns zero (0.0).
There is no standard specification on what happens when the
converted value would be out of the range of representable values by a double. 
See strtod for a more robust cross-platform alternative when this is a possibility.

This is less than ideal. If we wanted to check if an input even contained a valid numeric string (which we usually would) we’d have to work harder. The strtod alternative provides a means to do that, but we’d still have to do an explicit check after calling the function.  Other C-style functions use a return code to indicate success or failure.  It is also extremely easy to forget to check return codes, in which case the problem may only manifest itself in a crash later on, or worse, not at all, but instead just produce bad output.  These types of problems are very hard to track down and debug.  Using exceptions the program crashes precisely where the problem occurred unless the programmer handles that particular exception.

So to summarize: Each of your functions should have a well-defined job.  They should do only one job and do it well.  If they can’t do their job because of improper input, then they should raise an appropriate exception.  I think following that idiom results in cleaner, easier to debug code.  You could certainly still return complex data types where appropriate, but trying to incorporate success/failure information in a return type will often lead to difficult to debug errors when the programmer forgets to check the return status!

Mary Poppins

I just finished up Ruby – Day 3 from Seven Languages in Seven Weeks.  I can’t say I fully grok everything that I’ve done, but Ruby seemed fairly straight forward and in many ways similar to Python (though I have noticed Rubyites tend to be able to on a moments notice generate a long list of why it’s better than Python).

I had originally wanted to learn Ruby because I’ve been trying to re-launch my website and blog using nanoc, which is written in Ruby.  While strictly speaking it isn’t necessary to understand Ruby to use nanoc, any type of customization will require writing and manipulating Ruby code, and knowing me, I’ll want to do some customization.  I feel I understand enough of the syntax and a bit of the power-features of the language to get by understanding the bits of nanoc that I might be customizing, but I definitely have a long way to go with a lot more experimenting before I could say I’m comfortable with the language, but I suppose that’s to be expected!

Maybe it’s due to my very preliminary understanding of it, but I feel kind of let down by all the hype.  Perhaps it’s because it’s an OOP language, and so in many respects similar to Python, or even C++ in terms of how the code is structured, but I didn’t really get terribly excited about it as I was working with it.  I’m really looking forward to the other languages in the book and breaking away from OOP thinking.  From the descriptions, it sounds like each of them encourage a different type of thinking when writing code and designing a program.  Even though it’s last on the list, I I’m going to start the Haskell section next, mainly because I’ve already dabbled in it a bit, and I’d like to write up an assignment using a functional programming language, so I should probably learn enough of one to be able to actually write up a solution!