No floating point types is a little harsh, but IMHO one should only use them when they really understand their implications. Otherwise they might be tempted to do Bad Stuff like using float for currencies, etc.
It is defined and you can do it in C without using floats. On the other hand, even if you do use floats, arbitrary arithmetic is not invertible. In other words a / b * b isn't necessarily a, regardless of what types they are.
What do you mean division is not defined on the set of integers? Mathematically or programmatically? Because yes you have euclidean divisions which are divisions for the integers.
The first structured programming language I learned was a home grown language called Simpl-T, at the University of Maryland. It did not have floating point, but one could still do an awful lot with it. Other members of the Simpl-X family did have floating point, as I recall.
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].
C language is as simple as it gets. The only concept people struggle with is pointers, but even that is a very simple concept. Teaching something that is simpler than C to computer scientists will breed very poor engineers.
C is a very tricky language, because a lot of constructs that work when naively translated to assembly (on common CPUs like x86 or ARM) are actually undefined behaviour.
This is always brought up when "difficulty" of C for teaching is discussed but it will basically never be an issue in a programming college course. It is a problem that doesn't exist.
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.
While well-constructed C programs are as minimal and straightforward as programs come, the language is certainly full of pitfalls (just look at the underhanded C contest). Keep in mind that most computer science students in their first intro course have not yet developed the mental models that experienced programmers take for granted. Students will try doing all sorts of things which don't make much sense, and 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.
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.
>... 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.
I don't get the analogy. Every algebra course goes over the material in order of complexity. You don't go over real analysis until the students have mastered the basics.
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
You actually managed to change my mind and I now agree C isn't a good first language. Mixing C with data structures / algorithms in the very first programming course is probably not a good idea for the reasons you mention.
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 didn't "choose" to program before college because I didn't decide to major in CS until I applied to college. And I suspect that unless "WAY" means high-school, you programmed before college because of your parents.
Nah my parents are barely computer-literate even today. I found QBASIC that came with DOS when I was like 10 which had the source code for 2 simple games and decided I wanted to learn how to make stuff like that. A few years later I read about Linux somewhere and found someone who baked and mailed me Slackware on CDs for a small fee so then I had gcc
Why stop expectations for self-study at the programming part of the degree program? Math textbooks are available from any library. Rather than continuing mathematics from where high school left off, we could skip past the beginner stuff. The students who really care about computer science will have already learned the basics of discrete math, linear algebra and theory of computation.
Where did you get the idea that I said we should skip anything or that there should be "expectations" for self-study?
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.
If you're learning C, as a basic intro into programming concepts... you're gonna have a bad time. Try python. However, if you're learning C because you'll need it later for [any reason]. You should learn gdb, and valgrind (asan has replaced valgrind for most of my usage, valgrind is often my last ditch now) on day one... I guess I mean to say, if you have better things to teach than gdb, then C isn't the language you should be using.
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)
I would agree the point of college is to push you into unfamiliar territory.
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.
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.
Yes, if you look at it from a C coders point of view. You know exactly what your program is doing (excluding compiler optimizations).
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)
The Computer Engineering program at UT Austin teaches assembly (and architecture basics) for an academic RISC machine, then ARM assembly, and then C, in that order.
So it's not no one, even if it's not as common anymore.
It definitely makes C make more sense as an abstraction of asm.
I believe the CS school starts on Java.
Is this the program where you literally build a computer from scratch, starting with a transistor, and working your way up. If so, you're comparing a top down approach, to a bottom up. Each with a different end goal. My comments are only meant to apply to a intro to CS top down approach, with the goal of creating programmers, not complete system engineers.
Java was taught at my school because you don't need to learn much boilerplate to get started, the OOP parts of the language are relatively clean, memory management isn't manual,...and because they got a nice deal with Sun.
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?
That was meant a joke about Java being a worthless language.
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.
malloc is defined within the C1X standard document as part of the C standard library. If you mean that malloc isn't a language keyword, OK, sure. And there are cases where you'd write a non-trivial C program without any dynamic memory allocation. It seems misleading to say that it's not part of the language, though.
Maybe my brain is weird but I read Kernighan/Ritchie and had no problem grasping pointers. It's actually one of the simpler concepts compared to concurrency and others. But I know a lot of smart people who don't get pointers. I wonder if it's the teaching.
C is not simple, C is language with a limited syntax and constructs which is not quite the same. Being proeficient in C means learning correct manual memory management, macros, an obtuse std lib, a compilation tool chain and management tools like Make, autotools and co. This is absolutely not simple. And don't get me started on string management with C.
If you're willing to break compatibility with C, why not make a clean break and use a language that better supports the things you want? The authors go on (correctly) at great length about the difficulty of union types in C compared to an ML-family language; why not use an ML-family language? (My university used Standard ML for their first programming course and I see it as a good choice)
The course that uses C0 goes on later to C, and is literally titled “Principles Of Imperative Computation”. We use Standard ML for “Introduction to Functional Programming”.
I was an ECE and now regret not taking Intro To Functional Programming (I almost typed 150 by instinct). I was put off by the horror stories, and perhaps also by a lab I saw once that was basically a longform story about "pleasing Senpai".
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.
I thought this would be for kids, I don't see the point of a simplified language for college students.
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.”
>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?"
Just wanna add that we used C0 for a subset of the class, and most of the students jump into a more rigorous course which involves actually implementing malloc.
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
I guess I have no idea what I'm talking about here as I honestly don't know anything about teaching and my own experience is atypical (around 8 years programming by the time I started college). So my comment is basically noise I guess.
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.
It is garbage collected, and includes both a heap of safety features and better compiler messages. This is a very nice set of features for people learning about data structures and memory management.
Well, it is training wheels. The paper also argues that they should be removed, as students are expected to migrate to C during the course.
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.
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.
The course it was made for at CMU (15-122) is primarily about writing contracts for imperative programs, I guess they decided C0 was the best way to do that since students have to take C anyway in 15-213 afterwards. Robert Harper's blog he apparently pushed for freshmen to land directly in a SML course where they would learn both imperative and functional methods but for legacy reasons they all agreed C should still be taught but with an emphasis on correctness/contracts so C0 was born.
Although I learned to program past when it was popular, I can't shake the feeling that BASIC is the best way to teach programming. The basis of any computer is the sequential execution of instructions, and it's this model of computation that informs things like algorithmic complexity or Turing completeness. Even if teaching BASIC wasn't possible, I would still prefer to see a subset of x86 assembly taught than something ridiculous like "a simpler C" which obscures the core functionality of a computer with abstractions, without teaching a practical language.
The funny thing about BASIC is that it's MUCH closer to assembler (and, by extension, the logic of the CPU) than any other language. It gets a lot of flak for GOTO's and the like, but that's how you control logic flow in assembly. C is an abstraction that allows you to avoid control statements like GOTO and every higher level language just abstracts more away from you (memory management, static variables, strongly typed variables, etc)
>The funny thing about BASIC is that it's MUCH closer to assembler (and, by extension, the logic of the CPU) than any other language.
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.)
Yes, but my reply is to a person who advocates BASIC as closer to assembly language. In this context, "BASIC" ought to be understood as Ye Olde Line-Number, Unstructured programming, BASIC, not the well featured, structured programming Turbo Basic or Quick Basic...
... which, by the way, i enjoyed using a lot when I was a kid.
BASIC is pretty good. I went from that to 6502, then to 680x0 and then to C (then to FORTRAN and then right back to C). I had a fair number of co-workers (years later, you know, when I had a job) who struggled more than a little bit with pointers. They said that they never got them. I re-read K&R to refresh my memory as to the clarity of the author's exposition on the topic and found it crystal clear. I suspect that if I hadn't gone through assembly first, I might have had problems with pointers too?
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.
I knew some C before taking an assembly language class (and I'm young enough that assembly never came up as a thing to learn before hitting higher-level languages). I understood pointers (mostly), but the concept was still a little confusing, and I'd have to consider the correct syntax every time I needed to use them.
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.
Had precisely the same experience except it was for an Apple II project instead of NES emulation. Turned me into one of those a-holes who think everyone should start with assembly if they want to learn C.
I think there at least needs to be more instruction about how the language is implemented. I feel like I learned the use of the stack in function calls and the difference between stack and heap variables disturbingly late.
I found programming in C on a very small device with primitive memory management made that distinction abundantly clear. Heap variables are the ones that leak memory!
> sequential execution of instructions is a convenient lie at most
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.
Instruction reordering, branch prediction, speculative execution, and optimizing compilers would all be examples of details that are abstracted over by the model of sequential instructions.
"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).
Yes, BASIC is an abstraction that hides eg instruction re-ordering. But if you are working with abstractions anyway, then imperative programming loses that claim to a privileged position.
Heh, it’s slightly ironic you use the sequential programming model as the basis for modern computing when that’s less true each year, both at the language and the instruction level. A better way to term it would be interleaving data dependencies and computation.
Heh, in some ways llvm ssa is the closest you get to a natural programming language for the modern processor.
One can see why they chose a more-safe version of C for an intro course- beginner C programmers can fall into a wide variety of difficult to debug pitfalls (memory leaks, out of bounds reads and writes, undefined behavior) that can make the task of teaching and grading a nightmare.
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.
For everyone complaining about how they taught themselves pointers while in diapers so why can't college freshmen already do this stuff...
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.
But should a language based on C -- of all things, be taught?
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.
Computer science is (and must be) about programming.
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.
When I think computer science, I think things like the lambda calculus, invented before programming languages. Richard Hamming's work on information theory.
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.
I think what the prior posting was getting at (and this too is fuzzy and doesn't capture the full scope of CS) is that most of what you mention is algorithmic. Understanding, proving, and discussing algorithms requires describing and reasoning about computation using some form of notation. That doesn't require any particular programming language or pseudo-code notation. But it does require something to communicate with any kind of rigor.
1st, are you sure that's a good thing? I don't mean diversification, I mean graduating students who can't grasp concepts that experts are sure is easy/simple. Pointers, like everything else, is hard... until you understand them. (Perhaps CS 101 needs an additional prerequisite)
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?
Using a weird local subset of C as an introductory imperative programming language seems like a really bad idea to me, unless you're using your weird local subset of C as the source language for a compiler construction course.
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.
113 comments
[ 5.0 ms ] story [ 171 ms ] threadC0-specific resources can be found at http://c0.typesafety.net
(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.
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
[3] http://c0.typesafety.net/
[4] http://www.cs.cmu.edu/~15122/
[5] http://www.autolabproject.com/
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?
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?
- 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.
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.
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.
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.
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?
[0]: http://mwillsey.com/papers/cc0-thesis.pdf
There's just so many other good options.
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.