The Perfect Teaching Language

Garth Gilmour
6 min readNov 26, 2021
Beautiful design exemplified by the London Tube Map

Introduction

In my previous blog post I argued that Python is far from ideal as a language for teaching beginners. I must confess that the don’t poke the bear light was flashing whilst I did it, but there is a little Dougal in all of us.

So as penance let me define what the perfect teaching language is, or at least should be. I will list twelve characteristics of such a language, eight essential and four desirable. After that I will make some (relatively) dispassionate observations on how contemporary languages measure up.

Before we begin I would like to stress that we are talking about a teaching language for beginners. Imagine we have a group of 12–24 students, keen but inexperienced, who will have 6–12 ours of instruction time with us per week as part of their higher education. We can assign homework and reading, but for the majority of the time they will be studying in isolation.

Desirable Characteristics

Here are the desirable characteristics:

  • Tool support. Obviously we would like our language to have a powerful IDE with extensive refactoring support. There should be a declarative build tool and a comprehensive testing environment with support for a range of approaches (including TDD, BDD and PBT).
  • Juggling installations. It should be possible for beginners to install the runtime, core tooling and IDE without assistance. Switching between versions of the language should be straightforward, as should maintaining multiple versions on the same device.
  • Applicability. The language should be usable for a range of applications. Ideally browser, mobile, desktop and cloud should all be supported.
  • Adoption in industry. The language should be recognised across the software industry and carry weight in an interview process. Developers should be able to use the language directly in industry, or at least easily convert to a similar alternative.

Key Characteristics

Here are the key characteristics of our ideal programming language.

  • Integrated. The features of the language should fit together to provide a consistent approach. Where duplication exists it should be a deliberate choice and not a post-hoc retrofit. Like a good story or movie the ‘world-building’ achieved in one part of the language should not be frustrated or undone in others. For example collection literals should be provided, but collections should otherwise be regular types, subject to the same rules as others and capable of extension by the developer.
  • Comprehensive. Although coding in single-paradigm languages is an excellent discipline, a students first language should expose them to the styles they are likely to need in the workplace. For the sake of this discussion I am going to define these as imperative, object oriented, functional and generic. The language should provide the ability to code in each of these paradigms, and (as noted above) integrate them as seamlessly as possible.
  • Relatable. The language should not break from established conventions in our industry. The terminology used in the language should echo mainstream usage and not redefine common terms or make arbitrary substitutions. Exceptions can be made where a term is archaic and/or only used because of happenstance, such as replacing static with shared.
  • Succinct. Keywords and special symbols should be used judiciously. There should be the minimum number of reserved words, but crucially the same keyword must never have multiple meanings. Examples of violations would be class in C++ and extends in Java. Ideally this should also be true of symbols, but concessions are inevitable due to practicality. Where symbols are reused there should be some higher theme that unites the different meanings, such as with the underscore in Scala.
  • Goldilocks Typed. There must be a unified type system with no special cases. Any distinctions made internally between value and reference types should be hidden from the developer. The language must require a keyword or symbol on every declaration and every variable must have a defined type. Type inference should be used judiciously to ensure that static typing does not become an impediment. Conversely type inference must not be so pervasive and insidious as to cause confusion.
  • Scalable. In recognition of modernity the language should enable its runtime to take advantage of multicore and cloud hosted hardware. Variables should be immutable and evaluation lazy by default. Ideally it should be possible to partition the codebase into areas that are pure and areas that have side effects.
  • Nimble. Learners should be able to work within a fast feedback loop, where they rapidly see the consequences of changes to their code. Compilation and deployment times should not cause the student to loose focus. Ideally the build / deploy / test cycle should be ongoing and seamless.
  • Challenging. The language should not require the use of advanced features to accomplish everyday tasks. Nor should commonplace errors be mistaken as attempts to use advanced features. However, the language should offer an open ended learning path, where it is possible to extend both the language and runtime.

Thoughts On Existing Languages

Here are my thoughts on how current languages measure up against these criteria:

  • There are a number of newer languages that build on past experience and score highly on integration, succinctness and scalability. In my copious free time I’m trying to learn ReScript and Elm, both of which I find very impressive. Sadly these languages lean heavily into purist FP and hence score poorly on comprehensiveness and relatability. They would be excellent for teaching FP in an accessible manner, but can be ruled out of this discussion.
  • TypeScript is a superb language, which has altered the way I think about code for the better. It gets bonus points in the comprehensive and challenging categories for bringing Structural Typing and Mapped Types into the mainstream. Unfortunately its biggest strength remains its biggest weakness, especially in the current context. By building on JavaScript it opens a Pandora’s Box of issues that we don’t want to inflict on beginners.
  • C# and Java are very comprehensive, do well on scalability and are improving at typing and succinctness. They are great choices for experienced developers, but fall down heavily on integration, relatability and succinctness. Both languages still have a lot of ceremony, juggle value and reference types, make it difficult to teach functional programming and have many deprecated features.
  • Scala would have been a contender ten years ago, but has acquired a bad case of technical debt and been pulled out of shape by the priorities of its community. Scala 3 is in many ways a new language, and goes a long way towards fixing these issues, but the pace of adoption is anyones guess. In five years it might be a different story, but for now we rule it out.
  • Rust and F# are the first serious contenders we encounter. Both would rate highly on all our desirable characteristics. They are rising in popularity, attract praise from new adopters and were recommended to me by readers of the first article. However, they are too opinionated (in different ways) to make the final cut.
  • This leaves us with Ruby, Go, Swift and Kotlin. I have to admit to being a huge fan of Ruby and believe it makes an awesome teaching language, especially with static typing available in Ruby 3. On the opposite side of the typing fence Kotlin and Swift check every box, with the advantage going to Kotlin when you include the desirable criteria. Finally, I cannot find a reason to exclude Go, even though it’s not my cup of tea. The educators I know who use it are big fans, and support for generics is on the way. So it deserves serious consideration.

Conclusion

A different way of thinking about the question is as follows. Imagine how you would feel if you were told tomorrow you had to teach ‘language X’ for a year, and that your salary would depend entirely on the student’s growth as developers.

Speaking personally I would be relaxed and confident with Ruby, Kotlin or Swift. Go, Rust or F# would give me pause, but the problem might be in my head and not in the language. I could make Scala work, but mostly because of long familiarity. With C#, Java, TypeScript or (shudder) JavaScript it would be time to roll up the sleeves and make sure the teacher’s desk is well stocked with Skittles and Espresso Beans.

I hope this summary gives you food for thought. Feel free to let me know where I went wrong along the way…

--

--

Garth Gilmour

Helping devs develop software. Coding for 30 years, teaching for 20. Google Developer Expert. Developer Advocate at JetBrains. Also martial arts and philosophy.