Is a simple, beginner style with named parameters and no unnecessary type annotations acceptable?

I believe teaching with explicit types for functions is just good practice as it serves as a way to annotate the code. This function takes this kind of arguments because… (e.g., it has to be a real number in order to compute the arithmetic mean).

1 Like

The problem I have seen here is that they may choose a much more concrete type than they really need, or too generic of a type (which is a problem for collections of nonconcrete things).

In the first case, the too specific case can make it so that auto differentiation, etc cannot be applied, and the too generic with collections can have horrific performance.

Types are great once you know what you are doing

2 Likes

The difference is really between the “teach students Java so they are explicit and know what’s going on”, vs “teach students Python so they can get things going quicker and get to the real meat of the problem”. In most universities, the switch to Python has occurred in the CS100 course. Proof by majority, but I assume that the people who made the decision did so quite consciously.

While I enjoy having had the experience of statically-typed languages, when learning algorithm theory, it was not necessary. Additionally, in a mathematics or scientific course, teaching programming gets in the way of the actual science. The ideal would be programming to be so easy that it’s never mentioned and just used as a tool. In that case, the easiest situation is probably the best.

5 Likes

The ironic thing is that type annotations actually seem more meaningful for the math and science context than they do for the algorithms context. If you are learning to write quick sort, you might think type annotations are silly because of course they are integers why would they not be integers let’s not worry about this now. In the math or science context however the distinction seems more relevant, especially because of the distinction between scalars and tensors. This isn’t programming getting in the way of science, it’s programming acting as a concrete instantiation of a mathematical abstraction. I’d argue much the same for things like various types of integers and strings. What is not important at this point is, e.g. the distinction between Float64 and Float32, but that’s not really what we’re talking about.

As for Python, when I first got introduced to it, I kept hearing people say things like “you don’t even have to worry about the types, it’s so great you just write natural code”. This is just silly. Of course you have to worry about types especially since when using Python you are immediately confronted with numpy objects or some other abstraction that really lives in C code. It didn’t take me very long to start getting inscrutable errors resulting from me passing some numpy array of the wrong rank or some other such silly thing (I was particularly vulnerable to some of the pitfalls as I was coming from C++). Suffice it to say when I started using Python I was not in the slightest bit impressed by the fact that every reference to data types is annoyingly hidden.

1 Like

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…

  1. 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] )
  1. 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
3 Likes

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 :slight_smile:

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.

1 Like

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.

1 Like

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!).

1 Like

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.

3 Likes

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.

3 Likes

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.

2 Likes