The best course I ever took (6.001) used Scheme (back in Fall 1980 [it was actually Lisp, Scheme started in Spring 1981], but we implemented a simple Scheme interpreter and compiler in Lisp) which could be used to demonstrate all the important concepts, without spending time trying to learn the intricacies of the syntax). That course (out of whose course notes came the classic “Structure & Interpretation of Computer Programs” textbook) had the philosophy of teaching concepts (data structures, interpreters, compilers, recursion, functional and imperative programming, metaprogramming, message passing, data abstraction and data oriented programming [OOP wasn’t really talked about too much back then, outside of Smalltalk and Actor, Lisp got Flavors in late 1980]) instead of trying to teach students how to go out and program in the “real” world (which would have meant Cobol, Fortran, PL/I, C or assembly language most likely back then!)
There was even a preference for students not to have learned other popular languages of the time (BASIC was common then for high school students to have learned), because there was a lot less they need to unlearn.
Here are my problems with getting people to use types before they understand generic programming…
- Overtyping restricts the ability to use cool libraries (e.g. auto-differentation, GPU, etc.
f(x) = x.^2
#Then pass to an AD library that does something internal like,
using DualNumbers
x = Dual(0,1)
f(x)
#Works great!
But if they went
f(x::Real) = x.^2
x = Dual(1,0)
f(x) #Breaks inside of the Auto-differentiation code, and they would have no idea why.
Of course, this is because (Dual <: Real) == false
… but finding the proper abstract type for this is tricky for new users. Moreover, these sorts of problems are nasty… putting in an unnecessarily constrained type anywhere in a huge sequence of library calls prevents the whole thing from using AD, GPU types, etc.
Another good one which breaks perfectly good code:
using StaticArrays
q(x::Array{Float64,1}) = x.^2
q([1.0 2.0]) #Throws an error unnecessarily
q([1; 2]) #They should go 1.0 and 2.0, but this is a confusing compilation error
q(@SVector [1,2] ) #Not a chance.
#But all these work fine without any problems.
q2(x) = x.^2
q2([1.0 2.0])
q2(@SVector [1,2] )
- It is very easy to accidentally setup arrays of abstract variables (which I think is a big performance problem)?
For example, the following may seem completely natural to users who see a type called Real
all over function definitions.
function test()
x::Array{Real,1} = [1.0; 2.0]
return x
end
But I think (1) is not really an issue: Most easy code gets away with one definition per function; that is, multiple dispatch is not actually needed, you only use specialization. However, in order to get any kind of performance, you need to know the difference between concrete and non-concrete types.
In other words: Annotating types in structs and inputs is essential; the fact that you can dispatch on types is nice, but maybe not necessary for beginners. And getting performant code in julia is too much fun to deprive students of
And then people need about as many type annotations as python code; with the exception that all classes have __slots__
and __slots__
have types (parametric types are probably for later as well).
(1) is a real problem, especially for trying to get code to interoperate between libraries. Just try to auto-differentiate some random code you find in julia.
But I am not sure there is a tradeoff with performance in most cases… the compiler and inference figures things out and compiles a version when it encounters a concrete type (in my understanding). We need to train people not to do type-unstable operations within functions, but that is different…
In a computing course (I’m imagining some manner of “computing for scientists”) I’m not sure why you’d want to introduce something like Grassmann numbers before students are able to understand things like !(Dual <: Real)
. Likewise, I’m not sure why you’d want them to be doing things where they have to worry about performance before understanding the difference between Real
and Float64
. Lastly, your q(x::Vector{Float64})
example throws an error for a math reason they are probably at least somewhat familiar with: the distinction between a vector and a vector transpose (i.e. the difference between a fundamental and anti-fundamental representation). Also this stuff has at least some general applicability, it’s not like asking them to memorize documentation for numpy!
Probably I am just badly misunderstanding your course (after all, I have no idea what it is!). Certainly if your course is primarily a math course with the coding as just some incidental demonstrations, I concede that my approach is completely wrong.
Anyway, I never felt like a particularly good TA in grad school, so perhaps I’m not the best person to take advice from on this matter!
Well he’s talking about computing for economists. And I’d say the same thing for computing for biologists. Going into mathematical details can be unnecessary tangents. If you want to teach linearization of dynamical systems, why force a detour through Dual numbers? Of course it’s better to know everything, but you have to stop somewhere.
Ok, point taken I guess. I’m still pretty dubious about introducing them to things that possess all sorts of complexity that you’re hiding from them before doing basic stuff, but certainly I have no feel at all for what should be taught in a biology or economics course.
The quetsion here is: can julia be a first language for both undergrads and grad students, or does it have to be a 2nd (or even 3rd) language. It is nice to have the power to do all sorts of fancy things when you need it, but if those fancy things are all over everyone’s example code, it is a problem.
To someone who has never coded before (and who really just wants to implement the correct algorithm with the minimum effort), syntactic noise is a major issue. Their brains are not yet wired the same way someone who learns Julia after already doing C++ TMP, Scheme, Python, BASH, etc.
I’m sure you were introduced to what DNA without being taught chromatin, histones, activator and promotor binding zones, and how to perform a ChIP-seq. What’s the point of telling someone exist without teaching them the basics of how it actually works? Of course, the issue here is the word basic: it’s basic to me because I know it. And just because it’s basic doesn’t mean it’s required for a good working definition.
Sure, sometimes you really need multiple dispatch (different source code, depending on types of parameters) and not just specialization (same source code, compiled for your specific input type). But this is really the only case where you need type annotations in function arguments, and the only reason for abstract types to exist as a visible language feature!
I would guess that this is not happening as often for beginners (if they know the types at call-site, just make two functions with different names).
But if you plan on teaching structs at all, you will need all fields concretely typed (because the compiler and inference won’t help you there). From this point, I would hammer on the importance of typing structs and possibly containers, as well as --most importantly-- the input to computations and postpone talking about type-based dispatch, multiple dispatch, abstract types and the possibility of restricting a function-definition’s domain of definition to specific types.
I was taught with Java. A lot of others were taught with C++. Honestly, I’m not sure the “Python first” approach is any good because by the second course you will want to start writing data structures and play with pointers, in which case by course 2 or 3 everyone always switches back to a lower level language. Julia has the ability to span those domains well, so it’s conceivably the only higher level language that can keep going after the first or second course (though you may not want to use it in an operating systems course, but Julia would be great for a compilers course!).
Thanks. I think that confirms my instinct.
I am hoping if it is possible to teach people how to deal nearly everything in a basic course without telling them how to define a struct
at all (even though they would have to use them). Named tuples seem to do most of what a basic user would need to get started.
For multiple dispatch, I think it is valuable to tell the students why the same library functions they are using may have the same name but do different things depending on what they pass in.
For what it’s worth, the first language I was taught was Fortran and I thought it was a great choice. (I had played with BASIC when I was a little kid, but it’s not like I had done it on a regular basis.)
That’s the sort of thing I meant when I was talking about hiding complexity. If you want to avoid talking about types I do think you need to be sure to emphasize this.
I would suggest checking out @mykelk’s upcoming “Algorithms for Optimization” book, which is fantastic and uses Julia code rather than pseudocode to introduce all of the concepts. If you email him, he’ll give you a link to the draft: ANN: Algorithms for Optimization book draft
The code stays very basic, mostly using if
and for
and so on for control flow, and almost no type annotations. It’s a very pleasant and readable “executable pseudocode” style — and there is a basic Julia introduction in the appendix that covers just the small subset of pieces that are useful to understand the programs in the book.
When it comes to a “first course in programming”, there is not much of a difference between undergrads and grad students; there should be few (if any) prerequisites.
I think that a subset of Julia makes a fine language for this course. It is the responsibility of the lecturer to select this subset, just like with any other topic. Determine the course goals, and pare down the language to a minimum to avoid distraction; this requires careful planning but it is worth it. The median student will be happy, and the students with a flair for programming will fill in the gaps later.
Completely agree. And I checked out the Algorithms for Optimization book which has exactly the sort of executable pseudocode style that would be easy enough to follow for most people.