Learn C and build your own Lisp
buildyourownlisp.com
buildyourownlisp.com
I've implemented a few simple implementations of basic (and not so basic) type systems[1]. Currently, only 3 type systems are finished (Hindley Milner's Algorithm W, and Daan Leijen's extensible rows and first-class polymorphism), while gradual typing is almost done (based on [2], in branch 'dev').
[1] https://github.com/tomprimozic/type-systems
[2] Jeremy G. Siek, Manish Vachharajani - Gradual Typing with Unication-based Inference - http://ecee.colorado.edu/~siek/dls08igtlc.pdf
I know the dragon book probably covers this, but I can't justify spending 130 USD on a book at the moment.
And I'm working in OCaml, so this is perfect.
Please consider putting out a donation link so people can support your work (Bitcoin would be most convenient for me, but I'll definitely try to donate regardless).
Also, some kind of open source license would be great (MIT or BSD, maybe?).
But I'd definitely be interested in that talk if you can remember the name of the speaker or the venue/conference so I can try to Google it.
> Please consider putting out a donation link so people can support your work (Bitcoin would be most convenient for me, but I'll definitely try to donate regardless).
I'd prefer comments/bugs/interesting discussion.
> Also, some kind of open source license would be great (MIT or BSD, maybe?).
I'll do that ASAP, in the meantime, it's public domain/CC-whatever, with the exception of file first_class_polymorphism/propagate.ml and propagation of types tests in first_class_polymorphism/test_infer.ml and first_class_polymorphism/test_infer.ml, which are heavily inspired by Daan Leijen's reference implementation.
Hope I didn't offend by suggesting donations -- wasn't my intent.
Anyway, thanks again for putting this out there. It will be a big help.
Edit: Wow, plzoo is another great resource, thank you. I found a reference to the plzoo project and website at one point but the site itself no longer seems operational ("no one here but us chickens", it says) and I didn't think the source code would be available anywhere. Very happy to see it's up on github.
http://alaska-kamtchatka.blogspot.ca/2009/01/essence-of-conc...
I'd definitely pick up a used copy of the Dragon book if I could find one for a reasonable price. Unfortunately, this book is used frequently in college CS courses, so the demand for used copies stays high, and so does the price.
http://www.cs.cmu.edu/~rwh/plbook/book.pdf
It gets pretty math-heavy at times, at least in the beginning (you can probably skip the first chapter if it's too rough), but ultimately the book is about programming language design and different ways of designing and evaluating typed (or un(i)typed) languages. It also looks at a large number of important languages including PCF, Typed/Untyped Lambda Calculus, System F, Gödel's T and ALGOL (the last of which is interesting because it uses what we now call the IO Monad).
I'm taking the class (15-312) right now, and as homework we typically implement an interpreter for some language using SML (OCaml would likely work fine for the general use case), and then prove a few things about the language. Our languages rely on our Abstract Binding Tree (ABT) infrastructure that we developed early on in the course. You can find the course page at http://www.cs.cmu.edu/~rjsimmon/15312-s14/index.html if you want to check out what we're working on.
It's very opinionated at times (usually against anything "dynamic", eg. typing and binding ;) ) but I think it's worth it for the clarity that it allows. Of course, Bob Harper has the clout to back up those opinions!
Also, if you're looking to add a static type system to a Lisp-like language, you should take a look at Shriram Krishnamurthi's Programming Languages and Interpretation, second edition, at http://cs.brown.edu/courses/cs173/2012/book/ . It's a free online book, and I'm currently using it as the text in my programming languages class.
[1] J Siek, W Taha - Gradual Typing for Objects - http://www.cs.colorado.edu/~siek/gradual-obj.pdf [2] T Wrigstad, FZ Nardelli, S Lebresne, J Östlund, J Vitek - Integrating Typed and Untyped Code in a Scripting Language - https://www.cs.purdue.edu/homes/jv/pubs/popl10.pdf [3] A Rastogi, A Chaudhuri, B Hosmer - The Ins and Outs of Gradual Type Inference - http://www.cs.umd.edu/~avik/papers/iogti.pdf
Often, the code is more complicated than I find reasonable, while omitting things that make a lot of sense in "real" code, and it's very hard to know as an outside reader what the exact motivation for each decision was, by the author.
A few such things that caused me to WTF:
The initial few examples use a pointlessly static and global line buffer, instead of declaring the line buffer where it's being used.
There is hardly any const in the code, even for cases where it obviously should (to me) be used, i.e. for variables that are never written once given their initial value.
A magic number (the buffer size 2048) is repeated in the code, and even encoded into a comment, instead of just using "sizeof buffer".
I do think I found an actual bug (on http://www.buildyourownlisp.com/chapter4_interactive_prompt)... the readline() implementation ignores its prompt argument and uses a hardcoded string, instead. The same function also does strcpy() followed by using strlen() to truncate the string it just copied; that really doesn't sit well with me.
/* Fake readline function */
char* readline(const char* prompt) {
fputs(prompt, stdout);
fgets(buffer, sizeof buffer, stdin);
size_t bufsiz = strlen(buffer);
char* cpy = malloc(bufsiz + 1);
strcpy(cpy, buffer);
cpy[bufsiz - 1] = '\0';
return cpy;
}I see two problems with your code: it's over-allocating, since it's going to do the truncation there's no need to add 1 for the termination, it balances out with the newline; and there's absolutely no point in using strcpy() when you know the length.
It's true, you're right, I was keeping it a bit in-line with the book, if you change the malloc to be one less, then you can't use strcpy since it'll write the null term to memory you don't have (It might actually work on some platforms since it's just one byte but it's undefined behavior). If you just memcpy the string with bufsiz then there's no issue though.
new int[2048];
than #define TWENTYFORTYEIGHT 2048
new int[TWENTYFORTYEIGHT];
Also, it's generally OK to use a "magic number" if you know that the constant will only be used in that one place, or that there is no way it will ever change. Where they're bad is when they represent undocumented constraints across a program (i.e. "this 64 and that 64 are the same and can't be changed independently".)It strikes me as a symptom of the "magic numbers are evil witchcraft" religion gone to the extreme. The whole point of banishing magic numbers is to improve code's clarity and robustness to change. I've seen
#define ZERO 0
and #define ONE 1
more times than I care to admit. I have yet to see the value of either change. #define BUFFER_LENGTH 2048
new int[BUFFER_LENGTH];But it depends how the code is presented. "Here's an example of..." is different than "Here's how you should do..."
The reason given was superior debug support. Debuggers have gotten better at handling #defines, but #defines are still second class citizens. I've personally worked with a debugger that could precisely find all references of an enum, but only grep for a #define. In code with lots of conditional compilation, that makes a huge difference.
Props for mentioning conditional compilation early. It's underrepresented in books but essential for real life.
https://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_...
A word of warning - the pdf version is not as accurate as the html version. Prepare for some head scratching if you use the pdf.
"Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp."
Cheers for your hard work. :)
Also, I liked the cat pictures and hope you'll add more of those in the next edition, perhaps.
That being said I'm not entirely convinced that implementing a programming language is the best toy project for learning C since one of the first things you have to write is a parser and we all know string handling in C is a pity.
That being said it looks very interesting, I know C quite well but I might read it for the "implementing lisp" parts.
However, if you're serious about learning C then I strongly recommend getting the K&R book [0]. It's short and quickly gets down to business --- the first chapter alone gives you a condensed but working overview of the language as a whole.
Even experienced C programmers seem to keep the book around as it's good as a reference as well.
I strongly recommend people to learn C. It's a small, beautiful and very powerful language and the lingua franca for language ABIs. In fact, many popular dynamic languages are implemented in C (Python, Ruby, Lua and countless others).
Your time will be well spent!
[0]: http://en.wikipedia.org/wiki/The_C_Programming_Language
Both K&R and "Modern Approach" lack that word "Modern" very much, yet are written with undertone "it's everything you have to know about C", when it isn't. Person who doesn't know C very likely doesn't know how OS works, what architecture layers are behind the software he uses every day. He just knows somehow he wants to learn C, but doesn't really understand what C is, yet he knows that virtually everything is written in C, and "everything" usually doesn't run in terminal, but processes and produces sound, images, video, can have GUI, use some external devices, run in parallel, run on GPU. Also, it's pretty obvious (especially if you've used some language like Python already) that nobody writes everything from scratch these days, but there're many libraries that proved to be useful.
And stuff like branching and cycles seems to be pretty obvious even for somebody without coding experience, as far as I can judge from what I've seen so far. Yet we have plenty books that spends 20 pages to explain "if" keyword and mentions every function in standard library (why?! it's 2014, people, we have cplusplus.com now!)and covers nothing in sense of what useful libraries are out there, what are these domains where you still should use C today (because feet to meter converter shouldn't be written in C today and you probably should choose Python if there's no specific reason to use C), what tools to use for testing and debugging and such.
And while it can be justified for K&R (jeez, how old is that book!) it's just ridiculous that book that has "Modern" (well, it was 2008, but still…) in it's title covers almost nothing of what person who wants to program in C should know today. Worse, you'll see that only after reading these 800+ pages.
"C the Hard Way" is a little bit better, but still not as good as this one. This one also isn't perfect, but is the best of what I've seen so far.
In fact, for me it seems to be the best thing about this book. It mentions everything you need to hear once to be able to use search engine and clarify anything you don't understand. So help yourself! You don't know syntax of "switch" statement? Google it (or duckduckgo, or whatever)! You don't know how to use printf? You've been shown the way to cplusplus.com already, take your time and find out everything you want to know about that function. Then come back for a new piece of information to think about.
The only two things I guess are missing is "make" (I'd mention it from the start to help everybody save some time, with gdb and valgrind) and a little bit more information about pointers from the very beginning, because searching for help about "free" function will be no good if you don't know about stack/heap allocations.
For real world projects, it's not a bad idea to implement your own memory manager (even if it's only a wrapper for malloc and free). For real world interpreters Lua (www.lua.org) is written in C, is quite widely used and is not too large - if interpreters are your interest. If you are into lisps Structure and Interpretation of Computer Programs http://mitpress.mit.edu/sicp/full-text/book/book.html contains a the instructions for writing a lisp interpreter in any language.
In particular, Lua uses no intermediate representation (there is no Lua AST!), which makes its compiler portion a quite confusing read. Its interpreter portion is no less confusing, being a register VM with a register window scheme and a non-recursive eval using goto and a separate call stack. Do not be fooled by "not too large" aspect. Lua is worth studying, but it is definitely not a beginner material.
I would think that either way you can learn C. Which way is better for you depends if you are more motivated by going through one long project, or if you prefer to have many small ones.
First off, a small note: our project used C to implement Scheme (a lisp dialect), so it's similar but not exactly the same as this.
I'd recommend starting off with reading about the principles and coming up with a solution to each of the problems on your own instead of following a specific pattern as outlined in the book. For example, in our project, we decided to learn about the C basics, then just figure out how to make it 'act' like Scheme if given an input. Eventually, we thought of doing everything in a linked-list style to make organization and recursion easier and more natural, but coming to this conclusion on our own was very helpful.
Another thing is valgrind. As far as I could find, the text only mentions valgrind in one paragraph, but it's an excellent tool to check for memory leaks and errors.
Also, as mentioned in the book, a bonus is adding in GC. This turns out to be a pretty easy and a fun exploration of the different techniques available if you try a couple out for performance.
Our code in case you're interested: https://bitbucket.org/adamcanady/cs251_interpreter/src
Currently this claims to be Lisp, but it is some strange version of it.
Using a Scheme or Lisp has a lot of advantages:
* one can compare it with other actually working implementations
* there is already a wealth of material to learn from or where code could be reused
* many books exist
* the language actually has already got some thought
A good example is the book on Scheme 9:
If you have an HP printer with a Postscript renderer and you can get an image of it, you can find a digitised photograph of him too :)
Over time it's accumulated other features: http://akkartik.name/post/wart. But hopefully it's still easy to understand, because it has thorough unit tests. If you have a question about some line of code you can try changing it. Seeing what tests break is a great way to get feedback as you learn, in my experience.
f_ = proc_self(lambda(v0));
t_ = proc(lambda(v1), f());
id_ = proc(v0, f());
pair_ = lambda3(op_if(v0, v1, v2));
For the moment I gave up on it though but maybe it might serve as inspiration ;)[1] http://www.meetup.com/London-SICP-Study-Group/messages/board...
Is there any tree building capabilities using C?
EDIT: Fixed now.
I welcome corrections/edits.
What made it special for me was discovering the link between symbolic computing and the lambda calculus. The kernel at the center of every Lisp is a substrate upon which languages are built. eval knows nothing about your program but can read some Lisp forms and turn itself into a machine that can execute it. This is immensely powerful. From a shockingly small number or primitives you could build, theoretically, any language you need.
The more superficial qualities, for me, are a distinct lack of frivolous, obfuscating syntax; its uniformity; and its direct representation. I don't have to maintain an exhaustive mental mapping of syntactic digraphs and operators, I don't have to remember precedence rules, I don't have to remember the grammatical distinctions between expressions and statements, and I certainly don't have to maintain a running interpreter/compiler in my head. Armed with a simplified mental model of the lambda calculus such as the substitution method is enough to work out what any program is doing (and indeed due to the nature of Lisps is also easy to interactively verify).
Every other language I've worked with requires memorizing a laundry list of special cases and weird rules (ie: how array and pointer parameters are changed by the compiler in C, precedence rules as mentioned, etc) imposed for efficiency's sake and often maligned with good engineering practices. Some strange monkey-brained part of me fools myself into believing I am becoming a better programmer when I pollute my mind with these things... but I've learned over the years that it's just a trick. The ideas are important and the implementation is just the painful cost we pay to make them reality.
Lisp just happens to have the least cognitive load in my case.
This helpful for small scripts which need to be scanned quickly, but anything non-trivial will already have a significant layer of abstraction which takes time to parse.
Lisp people love their macros. Clojure has fancy data structures and control flow. It will take time to understand these things whether its in lisp or sandskrit.
Perhaps... I'm not aware of any empirical study into the matter so my claim is mere speculation.
There are, for example, plenty of highly productive Perl and Haskell programmers. Those languages are notorious for the gobs of arcane syntax. And yet their proponents claim it's an advantage.
"Cognitive load," in my case refers to the amount of information about the language and its compiler/runtime I have to recall in order to estimate how a given piece of code will execute.
I don't see anything arcane about Haskell's syntax. There are no surprises in vanilla\* Haskell syntax - no cruft in the syntax of expressions, no complicated syntactic sugar, you can see what is and isn't an infix function at a glance, same with type constructors (capital letters), etc.
What might be arcane is some peoples use of user-defined infix operators. But I don't know if I would lump that in with ''Haskell's syntax'', since that isn't part of the grammar of the language. But YMV.
\* I can't speak for GHC extensions or template Haskell
I think the author confuses the concept (homoiconicity) and it's practical consequences. I think this quote from the comments below summarizes it:
> But the fact of the matter is that programmers interact with text, not with data structures.
Not Lisp programmers. It's trivial to implement an environment where code will be represented graphically and won't be ever represented as text.
That's, actually, the point: language semantics isn't tied to textual representation.fatal error: 'editline/history.h' file not found
Any advice would be greatly appreciated.
Other problem is that the code won't work with this library. You need to rework it using the following example: http://www.cs.utah.edu/~bigler/code/libedit.html
There's a lot more to set up and tear down when using the Mavericks' editline; it's not so user-friendly for a beginner as the code in the example.
And after that you include with #include <readline/read line.h> #include <readline/history.h>
A caption for a picture from chapter 5, preceding a look at the grammar of Doge - DogeLisp anyone?
I wonder why...
Any feedback is appreciated.
DOM loaded in 59 seconds.
Content loaded in 180 seconds and weighs 12MB
It's also downloading mp4 files in the background.
....
4 minutes passed and it is still working on the banner-video.mp4
Also, it has problems with scrolling (freezing). I use a MacBook Air 11'' from 2012.
Grandparent, currently your website has slurped 31.3 MB (and counting!) over the wire for a simple landing page. What gives man? Seems that everything which should be setup on your page already, is.
For goodness sake, when teaching C, make sure to check alloc's return values.
Almost any language can be (ab)used in an "unsafe" manner. Conversely, it is entirely possible to create elegant, efficient, and safe code using C, and there is no particular reason to not use C for this project. In fact, C is marvelous in its simplicity and is an excellent candidate for it.
On a scale of 1 to 10 I'd put my skill with C++ around a 6 or 7. For C I'd go lower, around 3 or 4. I appreciate the work you put into this, and I intend to read your book to help me improve my skills with C.
So thanks for your effort.
While I don't disagree with your comment, I'd like to point out that the Oberon language (used in pjmlp's suggested book) is even simpler than C. It's worth checking out if you value simplicity in your programming languages.
http://www.inf.ethz.ch/personal/wirth/Oberon/Oberon07.Report...
The links offered in this thread are wonderful, The University links are impressive. HN is a tuition free education.
Very well written, fun to read and immensly useful. Wish I had your book 10 years ago. Thank you.
As for your remark, I did teach back in 1998 - 1999 for first year CS students, Pascal and C++.