C0, an Imperative Programming Language for Novice Computer Scientists (2010) [pdf]
citeseerx.ist.psu.edu
citeseerx.ist.psu.edu
1st: Restricting the set of students who can succeed in CS 101 to those who have already done extensive programming is one way to make sure the field never diversifies.
2nd: Computer science is not (supposed to be) about programming. Let's teach them how to think before we teach them how to be language lawyers.
I suspect they do this to transition students to real C later... Won't that give them bad habits later down the line?
Seems like a lot of work just to avoid going with Java or Go. With Java, eschew inheritance (not a bad habit at all) and wildcards at first, and you're fine.
Why not? This variant is garbage collected, has array bounds checking, and doesn't allow C's undefined/unspecified behaviors.
It need not be about C, or present-day digital computers, or even anything to do with the von Neumann model.
But there must always be a formal notation for describing computations, and students of Computer Science must always learn to treat that notation with rigor and come to grips with its nuances.
Probably too they must always deal with the concept of indirection, and think about references to (objects that contain) other references.
I'll grant you, though, a language with a saner notation for describing pointers (and types in general) would be easier to learn than C, and could still be equally useful - if not moreso.
In the distributed systems world we have Lamport's work on vector clocks and Paxos, the CAP theorem by Brewer, the Domino paper from Amazon.
In other spaces we have the machine learning and neural networks, blockchain...
These are computer science, and they're certainly implemented with programming languages, but they were conceived (and often proved) independently of any implementation.
2nd, sure, then pick something easy, like [any of the thousand of other languages] Don't hand them a "broken" version of C, just because it's easier.
2nd part 2... What do you think CS is if not about programming?
C0 basically served as an introduction to C, with contract annotations. The contract annotations were used to enforce more thinking about your code (i.e. proving your code worked via pre/post/loop conditions). There were still pointers, there was just a bit of syntactic sugar surrounding it.
About 2/3's through the course we switched to full-blown C, at which point we had to learn pointers. The intro to computer systems course (think malloc, proxy, and buffer overflow labs) used C as well, and that in turn required us to use pointers. There's no "losing out" on learning pointers. It's basically a step-stool to C.
At the time, I had a pretty good grasp of pointers and thought C0 was a bit silly- I'd never used contracts in my life. I currently work as a full-stack web developer (bit of a twist from my intended path) and I've never seen anyone "write out contracts" like I did in C0. I now think it was a valuable exercise in that it forces you to define the parameters that allow your code to run.
So it seems to me that this course is trying to accomplish two things that are mostly orthogonal: 1) teach principles of computation, 2) teach where the semicolons and curly braces go.
#2 is a valid goal if you eventually want to ramp someone up on C/C++ without suffering language switching turbulence. But overall, I would expect that an intro course should be teaching fundamentals first. Language syntax is a completely secondary issue with respect to learning good imperative programming practices.
Languages come and go. Semicolons come and go. Yes, language switching causes speed bumps, I switch between C (embedded) and Python regularly. Twitchy-semicolon-pinky is a thing. Inventing another language to teach the basic corpus of imperative programming ideas seems like a lot of effort for little lasting payoff.
I agree that flow control with GOTOs, GOSUB and CALL is the closest to assembler, as well as line numbers slightly resemble how you place machine language code in memory (sequentially), however there are things in which BASIC (the classic line-number basic) deviates significantly:
1. Memory management is automatic in BASIC; in particular strings; string variables have automatic memory management; they can grow or shrink with no problem, you don't need to manage the memory.
2. The only way of allocating memory in basic is when you DIM an array; and then this memory block can't be FREE'd, because there's no "free" instuction, nor the option to DIM another array in the same memory location. (Typical BASIC gives no pointer manipulation facilities).
3. BASIC is often implemented as a strongly typed language. Even the syntax requires differentiating strings from numeric variables by virtue of the first character ($). There's no option to do declare a (void * ) variable and thus operate with total freedom from type checks, as it's possible in C. C is notoriously weakly typed, just as machine language is!
4. BASIC data types are higher level: Strings aren't really a data type in K&R C language -- what you have is character arrays that with some functions can be interpreted as null-terminated strings. Then you have integers and floats whose length (bit-width) or sign (unsigned, signed) you can't specify at all in BASIC.
There's also another significant deviation, if only for theorical considerations:
5. Machine language is arguably homoiconic: It is concerned with manipulating binary data (words), yet the language itself is made of such data (words). So machine language allows, for example, writing self-modifying code, code introspection, etc. BASIC is far from homoiconic.
I, personally, find C much closer to machine language (and thus, Assembler) than BASIC, for the reasons above, basically: manual memory management (via pointers), weakly typed and optionally untyped (via void* pointers and implicit casts), and it is geared towards manipulating data in the format that the CPU likes to (i.e. 32 or 64-bit integers, floats supported directly by the CPU, etc.)
... which, by the way, i enjoyed using a lot when I was a kid.
It might be snobbery, but it seems as if amongst computer programmers, there are those who "get" pointers, and those who do not. For whatever reason - I surely have not figured out how to explain pointers to those who do not "get" them. But I have found that this divide does simplify the software developer interviewing process down to only a couple of short technical questions.
Years later, I started doing things like writing an NES emulator and reading disassemblies of DOS games. Function calls, their relation to the stack, and pointers in general became much clearer after having contact with that much assembly.
> I surely have not figured out how to explain pointers to those who do not "get" them.
Makes me think about the difference between the data being represented and the method of representation. Sure, an exe's a binary, but it's also a data file if I choose to interpret it that way.
BASIC is one way. But you can also start with 'How to Design Programs' approach and start with Racket (http://www.ccs.neu.edu/home/matthias/HtDP2e/Draft/index.html).
Could you expand? Obviously some processors reorder instructions and try to do things in parallel, although this isn't true everywhere and the model of sequential instructions is still valid.
"Lie" might be too strong of a word, but "simplification" would be accurate. And sometimes that abstraction is leaky.
And probably more importantly, they're implementation details: you can't feed a modern processor out of order instructions, the entire system is built and optimised to efficiently execute sequential programs (but possibly several of them concurrently).
> And probably more importantly, they're implementation details
Well, right, programming languages are abstractions built on top of the actual implementation.
Heh, in some ways llvm ssa is the closest you get to a natural programming language for the modern processor.
- source: I’m a TA for this course
My iteration of Principles of Imperative Computation was... something. I liked the course but the professors who ran it that semester were really unpleasant. They accused a friend of mine for cheating when she pointed out an obvious mistake in someone's code during office hours when the line for TA help was basically on two whiteboards. She became a TA the following semester. ¯\_(ツ)_/¯
Page 18 says C0 is almost but not quite a subset of C. Though we strived to stay as close to C as possible, the array semantics proved impossible to resolve. However, it is still quite easy to convert a C0 program to C such that the meaning of the C0 program is one of the possible meanings of its C version.
...that's quitter talk.
No floating point datatypes
No ... switch statements or do...while loops
seem like terrible things to leave out, occasional gotchas of floating point not with standing being only integers means no division(As an aside nearly every language defines division on the set of integers this way so it’s not even like we’re making this up)
For that matter, addition and subtraction aren't defined on the range of fixed-bit-width integers.
1. Contracts in C0 bake preconditions, post conditions, and loop invariants right into the language. Contracts are only checked when in "dynamic recompilation" mode (basically debug mode). This allows you to check the correctness of your code more easily (and, importantly, in an easily gradable way).
2. The language is designed in such a way to introduce C to programmers that are used to other higher level languages. In the C0 phase of the course we introduce students to fundamental concepts like memory allocation, code reasoning and contracts, binary representations of numbers, and unit testing. Next we transition to C1 (included in the C0 install) which introduces typedefs and other features of C. At this point we focus on introducing data structures. Finally we make the final transition to C, where we talk about undefined behavior, gcc, stack vs heap, etc.
I've taken this course myself, and I would argue that this measured approach to learning C is far superior to throwing students (most of whom have no low-level programming experience) straight into C.
I agree that C0 on its own is overly simplistic, but the way it is used at CMU is as 'C with training wheels' - strict type checking, no potentially-unsafe pointers to the stack, dynamic array bounds checking, etc. Then, two thirds into the semester, students transition to real C, and learn how to correctly manage those potential pitfalls.
This is how students at CMU have been learning C since 2010, and graduates don't seem to be any worse off for it.
[Citation needed]
> C is overly permissive of these things unless you introduce additional tooling, e.g. valgrind, which can become very overwhelming for intro-to-CS students who are still feeling lost.
Sure, but isn't the the POINT of college? To push you into unfamiliar territory? To force you out of your comfort zone, and then hand you the tools to learn something new?
It may just be my arrogence speaking, having already taught myself C. But I taught myself C, when I had to fix a bug, I taught myself asan, and valgrind. None of that was very overwhelming. Complicated, very. Confusing, occasionally. But Once I learned them, they became easy, and intuitive, and helpful.
I think the problem I have with your argument, is; when you apply it to math, the argument becomes painful.
You don't want to learn algebra, it' too complicated; lets learn this subset of algebra, without irrational or imaginary numbers.
The problem with c is that the advanced things get in the way of the basics. You get students asking why their if statements didn't work and to answer you need to teach them GDB and valgrind and different compiler flags and how the stack works. This is a recipe for students that don't end up learning either thing. And it slows everything down so in the end of the year you can't cover everything properly and end up with mediocre C programmers you would never trust near a real C codebase
---
Another thing to remember is that we should be very careful about that drill sergeant mentality that intro to CS should be hard and painful. This advantages students that had the opportunity to program before university and often ends up turning away women and other minorities from the field
Although I am kind of miffed by the last sentence, I admit I'm one of the people who programmed WAY before college but in my experience most CS majors who didn't simply didn't choose to do so, as opposed to having lacked the opportunity to do so; you're dismissing the thousands of hours spent coding as a child (more than most CS majors invest in programming during college...) without the internet or anyone to teach me anything as "opportunity"
I have an issue with someone dismissing thousands of hours of invested effort as "having an opportunity". And the same is of course true for someone who went to the library and learned first year math.
I would gladly hand over some C code to someone who's really interested in programming, but has only ever written python. I can't say the same about someone who's only known Java. So experience with C, isn't really a good bar to judge programmers by, not by itself anyways. You don't need to know how the stack works for most issues in C, and after you can make you stuff compile, without asking questions, is the time to learn about compiler flags.
So if that's the only argument for using an obscure/broken version of C, then it's a crap argument. If you've been doing CTF for years, the school needs to offer you CBE, not force you into a class where you'll only break the curve, and still be bored.
Also, I'd gladly turn away women and minorities both. If you're handed a problem you know has a solution, and you give up because it's hard. Your skin color or sex is irrelevant, if you're the type that gives up when it's hard; you're worthless as a programmer. (But that might just be my obsessive need to solve any problem I don't understand, so I might not be the best judge)
But, that's why we have dedicated classes that challenge you. This language is meant for a course to try to bring everyone to the same level. Some students enter the course having done CTF's since middle school and others just took the AP exam. C0 is used only for one course, and the course that follows it uses C in depth.
>[Citation needed]
https://www.cmu.edu/career/about-us/salaries_and_destination...
In fact, C0 is explicitly targeted at computer scientists rather than engineers.
Two types of files, compiler directives, pointers, malloc and friends, are you really sure about that? Any non-trivial C program will use these, on TOP of the basic syntax that first timers have to learn.
There is a reason it is usually not taught as a first language, and Java, Python, C# or even Assembly are.
Java is taught a lot, not because it's better. But because once you learn Java, all you know how to do, is teach Java. Python is taught early because it's fun/easy, not because it's the best.
No one teaches ASM first anymore, what did you mean by saying asm is one of the first taught languages. (Maybe in some embedded systems, but generally that's not first, not by any reasonable definition.)
I think thd more accurate sentence would be "know exactly what the computer is doing". Lots of languages have clear and simple semantics that let you understand what your program will do. This is not the case for C though. The only way to understand what a C program does is to have a very complete mental model of the low level computer architecture, memory layouts, call stack organization and so on (aka knowing what the computer does). That is actually lot to go through for a first introductory course (hence why they are making a restricted language to teach before jumping to full C)
Python has similar arguments. It's a clean but powerful language that gets out of the way of learning basic programming, data structures, and such.
> But because once you learn Java, all you know how to do, is teach Java.
Why is that true for Java or Python, but not for other languages?
It's true about a lot of languages. But C isn't one of them. You can do a lot with C, you can do almost as much with with python and a lot faster as well. I can enumerate more, but those are the only two relevant ones.
With Java, you can get a job where job performance is measured in LOC.
Why is that?
I've tried to explain pointers to people who struggle with the very small number of pointer-related concepts, and I ... fail.
But I don't know why I am failing. These are not stupid people - and I speak the English language well enough - so those aren't the reasons...
The concept of pointers is simple, but their implementation has all sorts of subtle layers:
- interactions and apparent fungibility between pointers, arrays and strings
- how about: ( * (void( * )())0)();
- what about: * (p++) vs ( * p)++ or "++ * p", "* p++" and "* ++p" (Quotes for display reasons only)
- & vs * and various combinations of these
- pointers to pointers
- pointers to arrays of strings
- pointers to allocated memory that is/are not in scope anymore.
- Null pointer
- Many others!
At the very least, you want something with friendlier behavior in the face of errors than what C gives you.
Recursion?
My university opted to use C++ as the introductory programming language, since it is somewhat more safe. Now they are transitioning to even safer languages like Python and Javascript. While I don't agree with it, I can see why it is necessary to reduce the headache for instructors.
C0 seems like a good tool to use for teaching, although it may lead to bad habits later.
To wrap it up, in my opinion students should definitely be exposed to the concepts of the C family of languages early on: imperative programming, pointers, dynamic memory allocation, et cetera. All the pitfalls associated with these concepts also need to be explored.
The language doesn't necessarily matter as long as the students are allowed to make mistakes and learn from them.
C0 is used in 15-122, Carnegie Mellon's second semester introductory programming class. I am currently taking this class. The class teaches C0 for the first two thirds of the semester and then transitions to C for the final part of the semester.
C0 has essentially the same syntax as C, but for pedagogical purposes the semantics of the language have been changed to be much more similar to java. Instead of manual memory management, there is a garbage collector, and user defined structs can only be allocated on the heap. Additionally, there is only one integer type which is a signed 32-bit two's complement integer. Overflow is defined as wrapping. strings are opaque immutable types implemented at a language level, rather than just null-terminated buffers. C0 catches out of bounds array accesses at run time. While C0 internally stores array lengths to perform this check, the length is not accessible from C0 code so array lengths must be passed around just the same as C.
C0's biggest feature beyond C is that it allows for contracts. Functions can have preconditions and postconditions. There are also loop invariant contracts and assertions. These contracts are checked at run time when specific flag is passed to the compiler or interpreter (C0 has both, and uses a bytecode internally). A major focus of the class is proving the safety and correctness of the programs we write using these contracts. We are encouraged to prove safety and correctness using only logical reasoning from contract to contract, not operational reasoning. A co-requisite of the class is a course in proofs. There seems to be a heavy influence on the teaching style from Dijkstra's "On the cruelty of really teaching computing science" [1].
The 15-122 class has written assignments focusing on the proofs part of the course and programming assignments which for the most part are automatically graded by autolab [5]. Our code is tested against a number of unit tests to ensure its correctness and our contracts are also tested to see if they can catch bugs in faulty implementations.
C0's implementation is not open source, but it appears to be used in ongoing research [2]. Here is the language website and course website [3], [4].
[1] https://www.cs.utexas.edu/~EWD/transcriptions/EWD10xx/EWD103...
[2] http://www.cs.cmu.edu/~fp/papers/cc016.pdf
Learning about strange/engaging things, even if it's not directly useful?
Taking apart a new toy, just to see how it works?
If it's (allegedly) a problem at CMU, where isn't it a problem?
C0-specific resources can be found at http://c0.typesafety.net
There's just so many other good options.
However, if they as someone said use it in a compiler-writing course, for that purpose it makes sense to have a simplified language. I had to do that back in college and basically used a C subset for it (for the compiled language, not the implementation language).
“I am currently a TA for 15-122 at Carnegie Mellon, the class that C0 was originally made for. I would argue that C0 does an excellent job at breeding good programmers because of the following features:
1. Contracts in C0 bake preconditions, post conditions, and loop invariants right into the language. Contracts are only checked when in "dynamic recompilation" mode (basically debug mode). This allows you to check the correctness of your code more easily (and, importantly, in an easily gradable way).
2. The language is designed in such a way to introduce C to programmers that are used to other higher level languages. In the C0 phase of the course we introduce students to fundamental concepts like memory allocation, code reasoning and contracts, binary representations of numbers, and unit testing. Next we transition to C1 (included in the C0 install) which introduces typedefs and other features of C. At this point we focus on introducing data structures. Finally we make the final transition to C, where we talk about undefined behavior, gcc, stack vs heap, etc.
I've taken this course myself, and I would argue that this measured approach to learning C is far superior to throwing students (most of whom have no low-level programming experience) straight into C.”
Sure, I'd love to read this argument. I taught myself to program, started with python, then learned C. The hurdle of learning how memory is mapped, the knowledge/understanding gained by being required to clean up after yourself when you do *alloc. That's what makes learning C helpful. Perhaps I gave up reading the introduction to soon, but honestly, after I read it adds in GC to C.... I had the same reaction everyone else has already voiced. "Why use C here?" There's many other better languages if your just adding in hand holding.
Finally, if grading assignments was one of the defining reasons for inventing a new language, you need to take a step back from the text editor, and ask... "How did I get here?"
I agree with you that learning the ins and outs of memory is super important, but the methodology of this course doesn't imply the lack of teaching memory management
As long as you eventually require them to understand how things actually work and that code execution, memory management, etc isn't black magic, they should be fine.
C + a address sanitizer is better for learning about data safety.
It also does not make for a good language for the first introduction to programing. But if you structure your courses like the OP does, it's a great language to have.