$\begingroup$

In 2014, I would've said Python. In 2017, I wholeheartedly believe that the language to teach undergraduates is Julia.

Teaching is always about a tradeoff. On one hand, you want to choose something that is simple enough that it is easy to grasp. But secondly, you want to teach something that has staying power, i.e. something that can grow with you. The common dynamic languages (Python/MATLAB/R) all easily fall into category 1 due to their non-existent boilerplate code and the ease of opening up an interpreter and spitting out code, while C/C++/Fortran fall into the second category as the languages with which the core highly-performant software of today's world were written.

But there are issues with using a language which doesn't fully capture the other category. When using a language like Python, it nicely abstracts away things like types and integer overflow. This is nice for teaching the first semester computing, but as you want to dig deeper and deeper into how things are actually working, Python's language is too far abstracted away from the underlying metal to be a good teaching tool. But C/C++/Fortran (or Java... I learned Java first...) all have such a big startup cost that the hardest thing to learn is just how to get headers setup and main compiled, which distracts from actually learning to program.

Enter Julia. When you first use Julia, you can abstract away the entire idea of types and use it just like MATLAB or Python. But as you want to learn more, there is a "rabbit hole" of depth to the language. Since it's really an abstraction layer based on a type system + multiple dispatch over LLVM, it is essentially "an easy way to write statically compiled code" (and type-stable functions can actually be statically compiled). What this means is that the details of C/C++ are also accessible. You can learn how to write simple loops and functions without boilerplate code, and then dig into the function pointers. Julia's metaprogramming features let you directly access the AST, and there are macros which show every part of the compilation chain. Also, as a Lisp, is amenable to functional programming styles. And it has a lot of parallel computing capabilities. Ideas like parametric typing and type-stability are fairly unique and deep in Julia.

If you want to study programming languages themselves, you can learn the steps of how compilation works by using @code_lowered to see what lowering is, see the typed-AST with @code_typed , see LLVM IR with @code_llvm , and finally the native assembly code with @code_native . This can be used to show what the cost of dynamic variables is and exactly how "variable boxing" works, and this blog post shows how these introspection tools can be used to teach how compiler optimizations can/cannot occur.

There are not just computer science and software engineering ideas to explore, but also rich mathematical ideas. Since Julia's main libraries are written with generic typing in mind, it's trivial to create matrix-free operators and use IterativeSolvers.jl to perform GMRES using them. You can use introspection tools like @which to show you exactly how anything was implemented. For example, how does \ work?

@which rand(10,10)\rand(10) #\(A::AbstractArray{T,2} where T, B::Union{AbstractArray{T,1}, AbstractArray{T,2}} where T) in Base.LinAlg at linalg\generic.jl:805

That points me straight to the definition of \. It is implemented in Julia, so someone who knows Julia can then learn the algorithm and how it works through identifying matrix subtypes and specializing when possible (falling back to Gaussian elimination). Since Julia's code is MIT licensed (and almost all packages are MIT licensed), students are then free to use these ideas in their own code (with attribution) (when code is GPL licensed, as is the case with most MATLAB and R packages, they need to be careful about licensing issues!).

Since the language core is built with a very active open source community, there is also a rich resource on the history of the language development: its Github issues. Understanding language questions like what really is a matrix transpose? can be very enlightening for understanding these mathematical objects in greater detail.

But lastly, in the end, you want to teach your students how to create. Sadly, learning Python or R doesn't necessarily mean you have what it takes to "develop Python/R" since most of the widely-used and well-optimized packages have a substantial amount of C/C++/Fortran code in them in order to get performance. Thus, for these students to be able to contribute to the scientific ecosystems for these languages, they will eventually have to learn another language at some point. While that's not entirely awful, it's sub-optimal now that Julia exists. Since type-stable Julia is able to achieve the speed of C/Fortran, most of the packages in the Julia ecosystem are pure Julia code. Learning Julia means one has learned to develop Julia. And since Base Julia is also mostly Julia code (just a few primitives and the parser isn't), they can also contribute there as well.

That said, there are some downsides to choice of Julia. For one, it is much newer than these other languages and so it's a bit more scarce on resources. You'll have to come up with a lot of teaching tools on your own, or pull from resources on the web which are listed on the Julia website. Also, the language details aren't quite settled, though 1.0 is coming out soon (by the end of 2017). And it's also quite likely that you, the potential teacher of a course in Julia, might not have that much experience with the language yourself. However, these are the kinds of problems which go away over time, whereas the benefits of Julia that I mentioned above are much more core to the languages themselves.