
Learn C and build your own Lisp - p4bl0
http://www.buildyourownlisp.com/
======
tomp
In the last chapter, Bonus Projects, the author mentions Static Typing & type
systems. If anyone's interested in learning more:

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](https://github.com/tomprimozic/type-systems)

[2] Jeremy G. Siek, Manish Vachharajani - Gradual Typing with Uni cation-based
Inference -
[http://ecee.colorado.edu/~siek/dls08igtlc.pdf](http://ecee.colorado.edu/~siek/dls08igtlc.pdf)

~~~
jnbiche
Oh wow! This is extremely useful, thank you. I've been wanting to develop my
own statically-typed language, but almost all tutorials on the web are for
dynamic languages with little mention given to type systems. I've already
started a toy dynamically-typed language, but the step up from that to a
static language seems significant, particularly given the dearth of
information I can find on the web (aside from dense academic papers).

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

~~~
Nav_Panel
Here's a different book you might be interested in ("Practical Foundations for
Programming Languages":

[http://www.cs.cmu.edu/~rwh/plbook/book.pdf](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](http://www.cs.cmu.edu/~rjsimmon/15312-s14/index.html)
if you want to check out what we're working on.

~~~
oggy
I've been wanting to read that for a while. Another candidate is Pierce's
"Types and Programming Languages". Has anybody by any chance read both an can
compare them?

~~~
notthemessiah
Pierce will get you working in Coq ASAP, but Harper will provide you with a
much richer context.

~~~
me2i81
TAPL isn't Coq-focused. Pierce and his collaborators have another book
available online, Software Foundations, which is written as a Coq literate
program:
[http://www.cis.upenn.edu/~bcpierce/sf/index.html](http://www.cis.upenn.edu/~bcpierce/sf/index.html)

------
sanxiyn
This is a good stuff. 4 years ago, I wrote a Lisp in 500 lines of C in one day
after finishing SICP. Typing it out did made what I learned more concrete.

[https://gist.github.com/sanxiyn/523967](https://gist.github.com/sanxiyn/523967)

~~~
visarga
It might be easier to understand your program than to learn the language
directly.

------
unwind
I always find it so hard to help out proof-reading code for causes like this.

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)...](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.

~~~
cliveowen
If there's one thing that I really can't stand are magic numbers. The K&R says
to always use constants, which means this has been a good practice for more
than 40 years and I really can't grasp how someone can hope to _teach_
something when his own knowledge lacks the basics.

~~~
michaelochurch
In examples, magic numbers are OK. It's in large production systems (that will
require tuning, which becomes impossible with "magic numbers") that they're a
code smell. I'd much rather, in a small example, see:

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

~~~
john_b
In my experience, aliasing numbers to words has never improved any code in any
way whatsoever, and sometimes causes more headaches since numbers are easy and
natural to reason about numerically (big surprise...), while values such as
TWENTYFORTYEIGHT impose more mental overhead and open you up to typos.

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.

~~~
andyroid
I would say ONE_TB is an improvement over 1099511627776 when it comes to
"natural to reason about" numbers, but yes, defining ZERO and ONE is moronic.

~~~
nostrademons
A lot of times TiB is spelled 1024 * 1024 * 1024 * 1024 or 1 << 40, relying on
compile-time constant folding to compute the final value. I usually find that
a lot more readable than remembering the actual number.

------
blt
I dislike the use of a parser generator libary. If it were any other language,
sure. But Lisp is so easy to parse. Writing parsers is fun, and it would be a
good exercise. I applaud you for wanting to teach beginners how to install
libraries, but this seems like the wrong choice.

Props for mentioning conditional compilation early. It's underrepresented in
books but essential for real life.

------
gmfawcett
If you're interested in this, you might also enjoy "Write yourself a Scheme in
48 hours." It uses Haskell rather than C as the implementation language.

[https://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_...](https://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_Hours)

~~~
rasur
I am working through this slowly - it is excellent (although I find myself
referring to the Haskell report tutorial too, when I find something that needs
further clarification).

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.

------
rui314
If you are interested in this, you may find one of my Lisp implementation
interesting too. It's implemented less than 1k lines of heavily-commented C,
but it even supports a macro system and copying GC in addition to the common
features you would usually see in small Lisps.

[https://github.com/rui314/minilisp](https://github.com/rui314/minilisp)

------
anateus
Sounds like this may subvert Greenspun's Tenth Rule:

"Any sufficiently complicated C or Fortran program contains an ad hoc,
informally-specified, bug-ridden, slow implementation of half of Common Lisp."

[http://en.wikipedia.org/wiki/Greenspun's_tenth_rule](http://en.wikipedia.org/wiki/Greenspun's_tenth_rule)

~~~
acqq
Actually the project depends on the library which claims to implement the
named rule:

[https://github.com/orangeduck/mpc](https://github.com/orangeduck/mpc)

------
JasonFruit
If I decided to work through this, and thought it was really good, I'd be
unable to make a donation because I don't dabble in cryptocurrencies. Why not
make donating "real" money possible, as well?

~~~
jafaku
It's called "fiat", not "real".

~~~
JasonFruit
Not all non-cryptocurrencies are fiat currency.

~~~
jafaku
I doubt he was talking about precious metals...

------
EdwardDiego
If the author is reading this, thank you very much - I've always wanted to
experiment with C, but never had a project to use it on. Writing a Lisp
interpreter (compiler?) is not one that occurred to me, but it definitely
interests me.

Cheers for your hard work. :)

------
mkhattab
Thank you for writing this and sharing it for the common good. I've been
wanting to relearn C since I've switched to Python for work. Hopefully this
book will be good practice because I've been wanting to implement a Lisp
interpreter on a micro controller for quite sometime. I have plenty of
PIC18F4550 micro controllers lying around.

Also, I liked the cat pictures and hope you'll add more of those in the next
edition, perhaps.

------
userbinator
Someone needs to make a complementary "Learn Lisp and build your own C."

------
CGudapati
How good is this book for a complete beginner? I am starting to learn C in my
free time and I have no objective way to judge this book.

~~~
simias
Skimming through the first chapters of the book I can say it doesn't seem to
assume any prior knowledge of C or even a background in programming, it
explains all the basic concepts (conditionals, types etc...)

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.

~~~
orangeduck
Author here. Actually a parser combinator library is used for the parsing. As
you mention, I think there should be some interesting things inside for people
with any experience in C.

------
z3phyr
There is one excellent book I would recommend to everybody ->
[http://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_H...](http://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_Hours)

------
adamcanady
At my undergrad institution, we have a class that does a project very similar
to this. I recently completed the class and project and have a bit of advice
for anyone looking to take up this challenge on their own.

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](https://bitbucket.org/adamcanady/cs251_interpreter/src)

------
lispm
Fine to implement something. But I would wish that the language implemented
actually looks like Lisp or Scheme.

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:

[http://www.t3x.org/s9book/index.html](http://www.t3x.org/s9book/index.html)

------
SixSigma
On the compiler course taught by Brucee (Bruce Ellis - one half of Mark V.
Shaney - the other half being Rob Pike) he got you to do two projects : write
a Lisp implementation in C, and a C compiler in Lisp. Most instructive.

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

------
swah
Would tcc be good enough a compiler for this? Its very easy to install on
Windows compared to Mingw/cygwin etc.

~~~
sanxiyn
Yes.

------
akkartik
In similar vein to tomp's comment: if someone's interested in the bonus
project of implementing macros, I've implemented a simple lisp with first
class macros (fexprs):
[http://github.com/akkartik/wart](http://github.com/akkartik/wart). Moreover,
you can write anonymous macros out of more primitive constructs.

Over time it's accumulated other features:
[http://akkartik.name/post/wart](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.

------
mcescalante
I've already programmed quite a bit of C and C++, and this seems like a great
read + resource. I'd be curious to know how a non-developer or somebody coming
from something like Ruby or Python that had never developed C felt about this.

------
wedesoft
I was trying to implement an interpreter for a minimal (lazy) language in C
[1]. It uses Church encoding and other encodings. E.g. here are the
definitions of false, true, identity, and pair:

    
    
        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]
[https://github.com/wedesoft/blc/blob/master/src/x.c#651](https://github.com/wedesoft/blc/blob/master/src/x.c#651)

------
arcatek
PL101[1], from Nathan's University, is also a pretty good way to learn the
basics of programming language compilers.

[1] [http://nathansuniversity.com/](http://nathansuniversity.com/)

------
quakkels
Thanks for writing this. I find your writing style to be very easy to follow.
I've wanted to play with a toy language for some time, so I'm very excited to
dive into your book with both feet. I'm working on Windows and am up to
chapter 6. Very enjoyable. The only suggestions I would offer is perhaps to
mention that AST stands for Abstract Syntax Tree. It seems this book is
targeted to less experienced programmers, so it may be helpful to mention what
AST means in this context. Thank you for your hard work and willingness to
share your knowledge.

~~~
quakkels
Well, just made it to the next chapter where you talk about AST. :-)

------
ilbe
Wow, very timely, yesterday I needed to brush up on C and was wondering how I
would go about it. Thanks. Very minor note: the link to "Learn C the Hard Way"
on the faq page links back to the faq page.

~~~
orangeduck
Thanks. I've fixed the link.

------
zafka
Very Cool, I don't want to spend much time here now as I am at work......, but
I love the concept. I have been using C for almost twenty years, and I think
this would be a fun check.

------
pflanze
I've just begun starting a group to create a Scheme implementation from
scratch in London (UK). If you're already somewhat proficient in Lisp/Scheme
and interested in participating, see [1] for further info and send me a
message through meetup.com or through the link on my contact page.

[1] [http://www.meetup.com/London-SICP-Study-
Group/messages/board...](http://www.meetup.com/London-SICP-Study-
Group/messages/boards/thread/43167122/#118559022)

------
kyllo
Thanks for sharing, this is really helping me to understand in a little more
detail how compilers work. And the doge example gave me a good chuckle.

------
codemonkeymike
I had made a lisp interpreter using this book I found in my schools library.
[http://www.amazon.com/Data-Structures-Advanced-Approach-
Usin...](http://www.amazon.com/Data-Structures-Advanced-Approach-
Using/dp/0131988476/ref=sr_1_1?ie=UTF8&qid=1396656827&sr=8-1&keywords=advanced+data+structures+in+c)

------
sgy
Of course C is not the only option, and perhaps not an option at all for
newbies. JavaCC can be a good medium to build your own language too. They
recently added the ability to generate C/C++ code
([https://java.net/projects/javacc](https://java.net/projects/javacc))

Is there any tree building capabilities using C?

------
quantabytes
Your Bitcoin and Dogecoin addresses are mixed up.

EDIT: Fixed now.

------
stantona
This is extremely cool, for someone with some C experience but wants to dig
into writing a lisp.

------
matt_heimer
There is also a pretty good Compilers MOOC going on right now
[https://class.coursera.org/compilers/lecture/preview](https://class.coursera.org/compilers/lecture/preview)

------
gregburd
My favorite small and understandable implementation of Lisp in C is by Ian
Piumarta. [1]

[1] [http://piumarta.com/software/lysp/](http://piumarta.com/software/lysp/)

------
jameshk
This is awesome, I will certainly be using this to learn more Lisp.

------
crnixon
Is the source of the text on GitHub or elsewhere? I'd love to send pull
requests for some of the things mentioned in these comments, like magic
numbers not being in constants.

~~~
orangeduck
Hi crnxion. The source can be found here:
[https://github.com/orangeduck/BuildYourOwnLisp](https://github.com/orangeduck/BuildYourOwnLisp)

I welcome corrections/edits.

------
donniezazen
I am new to programming. What is it so special about Lisp that I keep hearing
about it in Hacker's News (often in Machine Learning and from old
programmers)?

~~~
agentultra
You will get different answers from different people. There doesn't appear to
be any commonly accepted universal truths in Lisp (otherwise we'd all be
programming in some variant of it by now). However there are plenty of good
ideas which many more popular languages have adopted.

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.

~~~
platz
I agree with everything, though the idea of least cognitive load I think only
runs skin deep (syntax).

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.

~~~
agentultra
> the idea of least cognitive load I think only runs skin deep (syntax)

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.

~~~
kazagistar
Enough use of macros means cognitive load for even simple code can be massive
though. You can't really know what the result will be, especially if the
entire program can be rewritten in some cases by a macro. If you constrain
their use enough, sure, you will not run into these problems. But in any
language it comes down to cognitive load vs expressiveness, in how you chose
to wield it. Generally, more dense code requires more thinking to understand,
and the resulting complexity has to do with the underlying structure, rather
then the particulars used to represent it.

------
osho
Can i get an pdf/epub version of this book?

------
skittles
Anyone know why clang on mac os x doesn't know about edit/history.h? The line
editing portion of the book requires it.

~~~
jdp
That would be for libedit:
[http://thrysoee.dk/editline/](http://thrysoee.dk/editline/)

~~~
jdstraughan
I've installed this, but I still get this error:

fatal error: 'editline/history.h' file not found

Any advice would be greatly appreciated.

~~~
melonman
Are you using Mavericks? You need to import the library using #include
<histedit.h>.

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](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.

------
aaronsnoswell
Got halfway through this today - most fun I've had programming in ages. Thanks
for the link and thanks to the author!

------
NAFV_P
> _Cat • cannot speak or program_

A caption for a picture from chapter 5, preceding a look at the grammar of
Doge - DogeLisp anyone?

------
leondutoit
Awesome. Hope I can make time to go through it - looks like good fun. Thanks
for putting it out there.

------
SneakerXZ
Does it use a generator to generate lexer and parser or author is suggesting
to write them manually?

~~~
orangeduck
Author here. A parser combinator library is used to do the parsing.

~~~
SneakerXZ
Too bad, always everyone who teaches how to write a programming language uses
generators but actual production programming languages never use them. Except
of a few langugages.

I wonder why...

~~~
kevinnk
It's hard to generate high quality error messages with current generators
unfortunately. More common nowadays is building first with a generator and
switching to recursive decent when it becomes too unwieldy.

------
yk
You should perhaps submit it to r/C_Programming/ since you looked there for
beta readers.

------
elwell
This looks really cool. _I wish I had the time to read it._

~~~
aidenn0
Out of curiosity, what are you doing with your time that is currently more
important than reading it?

~~~
elwell
Working on my startup, a social photo/video aggregator that organizes by
event, and has B2C and B2B aspects: [http://wesawit.com](http://wesawit.com)

Any feedback is appreciated.

~~~
visarga
It' slow.

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.

~~~
zenlikethat
I get DOMContentLoaded in ~1.41 seconds on from here, but the background video
downloading is worrisome.

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.

------
halayli
lval* v = malloc(sizeof(lval)); v->type = LVAL_QEXPR;

For goodness sake, when teaching C, make sure to check alloc's return values.

------
notastartup
Using this guide, is it possible to create a programming language in a non-
English language?

~~~
Jemaclus
The basic principles apply whether you're using English or not. You should
theoretically be able to write a programming language in any real-world
language you want.

------
JoelMcCracken
Can anyone briefly explain what makes the lisp that the author develops
unique?

------
pjmlp
Please don't. Rather pick something like "Compiler Construction" from Niklaus
Wirth and learn how to write compilers using memory safe system programming
languages.

[http://www.ethoberon.ethz.ch/WirthPubl/CBEAll.pdf](http://www.ethoberon.ethz.ch/WirthPubl/CBEAll.pdf)

~~~
orangeduck
I wrote this book over several months. It took a few days to get the courage
to share it online. It probably took you about 20 seconds to not read my book,
dismiss it, and post a comment telling other to do the same. If you think an
alternative is better that's fine, but how about you take an extra 20 seconds
to think about the situation next time you are going post a comment.

~~~
Iftheshoefits
The person you're responding to has a history of hating on C around here. I
wouldn't take what he wrote personally (easier said than done, I know). That
said, I offer the following:

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.

~~~
groovy2shoes
> In fact, C is marvelous in its simplicity and is an excellent candidate for
> it.

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...](http://www.inf.ethz.ch/personal/wirth/Oberon/Oberon07.Report.pdf)

~~~
iiiears
@OrangeDuck - Just wanted to thank you. Your style is clear and ideas progress
easily from the first chapter onward.

The links offered in this thread are wonderful, The University links are
impressive. HN is a tuition free education.

