Home > News and views > View all

Language, truth and logic

Published: Nov 10, 2016 5 min read

STEM learning

Written by: Greg Michaelson, Heriot Watt University.

It’s curious how discussions about teaching programming often descend into arguments about the merits of different programming languages, especially for beginners.

Right now, the main divide seems to be between visual languages, like Scratch, BYOB and AppInventor, and textual languages, like Python or Java.

Visual languages are favoured because the development environments offer “quick hit” coding of simple interactive programs that are rich in graphics and sound. However, they tend to provide a severely restricted range of programming constructs, and scale poorly to more substantial problem solving.

The advantage of textual languages is that they offer a seamless path to full strength programming. The disadvantage is that they are overwhelmingly complex for beginners, involving the rote use of poorly understood libraries and boiler plate code, to get even simple programs to do anything interesting.

I’ve long been a proponent of declarative languages, like Standard ML or Haskell, or even Prolog, because they offer pleasing correspondences between programs and data, through case structured functions driven by pattern matching. But they depend heavily on recursive processing of recursive structures which teachers, raised on imperative languages, seem to find unnatural. They’re also poorly suited to developing interactive programs.

Thinking about it, though, maybe the choice of first language really doesn’t matter so much. People retiring today, after, say, forty year careers, will have started off programming in what are now regarded as dead languages. The majority will have been trained on-the-job in Cobol or Fortran, or assembler for ancient architectures. Alternatively, the minority, who learnt to program at University or College, may have started off on one of the Algols or Pascal.

Yet all these people will have enjoyed long and varied careers, continually reskilling as languages and platforms changed. So those first steps, in a world without mice, clearly stood them in good stead for developing software for the pervasive, ubiquitous computing we enjoy today.

Now, despite all the rhetoric of separating design from coding, experienced programmers tend to use a language directly as a tool of thought, with design as a post-hoc rationalisation.

I think that what they learnt was as much about problem solving as programming, that is how to use the constructs in a programming language, no matter how impoverished, to represent and animate models of problems. Along the way, they acquired key programming abstractions, of which the most important are those of:

  • a variable as a binding between a name and a value
  • a type as characterising a set of values and operations
  • a control structure as a way of sequencing changes to variables and values, and
  • a sub-program as a parameterised control structure.

Now, despite all the rhetoric of separating design from coding, experienced programmers tend to use a language directly as a tool of thought, with design as a post-hoc rationalisation. Thus, one’s first language strongly colours how one feels about other languages, as it is how one first learnt to think through solutions to problems. Familiarity breeds contempt.

Furthermore, one’s first language involves a confusion of syntax and semantics, such that it’s hard to stand back and see the woods of problem solving from the trees of programming. Thus, acquiring a second language seems particularly hard, as it involves learning new ways of expressing things that we already know how to express.

Perhaps this is actually an argument for learning two languages close together from the start. As with growing up in a bi-lingual household, this may hold back short term proficiency in either language. However, the longer term gain is acquiring not just fluency in both languages, but also an appreciation of the wider problem-solving abstractions they both embody.

In my experience, teaching pairings from different paradigms strongly aids this. For example, learning assembler at the same time as or soon after a high level language brings an appreciation of how complex constructs are implemented down on the platform. Alternatively, learning a declarative language soon after or even before an imperative language gives an understanding of how abstract relationships between inputs and outputs can be efficiently and effectively realised. 

Now, teaching two languages at once may seem like a step too far. Goodness knows, covering problem solving and programming at the same time in just one language is hard enough.

But, if teaching is driven by computational thinking, then the overarching programming abstractions are every bit as important as their realisations in some specific language’s syntax and semantics. Thus, I think that constructing programs in complementary languages, to solve the same simple problems, is a good way to draw out these deeper abstractions, through the diverting dissonances in how they’re realised in each language.

You may also be interested in...