Nim Day 2: Hello Gophers!
============================================================

Hey, good news!  I have an end goal for this little series: Write
a (very) simple Gopher server and Gopher client!  Seems
appropriate for a phlog, and the Gopher protocol is famously
simple, so it's a great beginner project.

Today we install Nim, write the traditional "hello world" program,
and take a quick tour of the language.


Installing Nim
------------------------------------------------------------

First, head on over to Nim's website:

       https://nim-lang.org/

Navigate to the Install page.

(By the way, I'll focus on the UNIX install, but Nim is a first
class Windows citizen as well.  A binary and instructions are
available on the Install page.)

As I write this in 2018, Nim provides an installation utility for
UNIX-like environments called `choosenim`.  If you trust the Nim
developers, you can follow the instructions to run the script
directly from the 'Net (you do *not* need superuser (root)
privileges) and Nim installs itself.

Once that's done, you're left with instructions to add the Nim
install dir to your $PATH.  Here's what I did (replacing <me> with
my username):

       echo 'export PATH=/home/<me>/.nimble/bin:$PATH' >> ~/.bashrc
       source ~./.bashrc

Now you're ready to create a Hello World program.


Installing Nim for the paranoid (or security conscious)
------------------------------------------------------------

However, you're too crafty to do something risky like running
unknown scripts, right?  So you're going to do it like this:

       $ cd /tmp
       $ curl https://nim-lang.org/choosenim/init.sh > getnim
       $ less getnim

Now you can review the script before running it.  You can confirm
for yourself that it does as it claims:

       # This script performs some platform detection,
       # downloads the latest version of choosenim and
       # initiates its installation.

(I think one of the most impressive things about this script is
seeing the huge number of OS and hardware platforms Nim explicitly
supports!)

Bad news for the paranoid, though.  Turns out that this script's
job is to download the binary installer `choosenim` (written in
Nim, of course) from Github and run *that*.

You can read the source for `choosenim` here:

       https://github.com/dom96/choosenim

But ultimately, you either trust the binary or you don't.

So for the truly paranoid, you're going to need to go the source
tarball installation route (which is also on the Install page.

But are you *really* going to audit all of the source before you
compile Nim yourself?  At some point, you have to trust something.

(Hmmm...this is starting to get philosophical and we have a lot of
ground to cover or we'll never get that Gopher server or client
written.  And that would be sad.  So let's assume you've picked
the best installation method for you.  Me?  I trust the Nim devs.
Beyond that, I have backups and my secrets are encrypted.)


Hello World
------------------------------------------------------------

First, let's make sure you have the compiler successfully
installed and in your $PATH (or Windows equivalent)

       $ nim --version

I stick with tradition with new languages: the first thing I write
is always a variant of Hello World.  So let's type one up and see
if we can compile and run it.  So fire up Microsoft Word and let's
get programming!

Actually, if you like, you might want to install a Nim-specific
plugin for your favorite editor.  (I'm using this with Vim:
https://github.com/zah/nim.vim (installed via Vundle))

Create a new file:

       # hello.nim
       # As you can tell, these are comments
       echo "Hello my little gophers!"

Compile this treasure:

       $ nim compile hello.nim

Run it:

       $ ./hello
       Hello my little gophers!

**Woo!  Time to celebrate with a big ol' can of beans!**

Since we have something to examine, let's take a peek at what was
created for us.  First, how big is the executable?

       $ du -h hello
       204K  hello

Ouch!  That's pretty big!  Ah, but that's a development build with
"runtime checks" and no optimizer.  We can probably do better with
a "release" build:

       $ nim --help
       ...
         --opt:none|speed|size   optimize not at all or for speed|size
                                 Note: use -d:release for a release build!
       ...

       $ nim compile --d:release hello

Now how big?

       $ du -h hello
       88K  hello

Ah, much better.  Not that it matters, but can we reduce that even
more?

       $ nim compile --d:release --opt:size hello
       $ du -h hello
       44K  hello

That'll do.  Of course, the equivalent C is smaller:

       $ cat > chello.c
       #include <stdio.h>
       int main(void){
         printf("Hello World\n");
         return 0;
       }

       $ gcc chello.c -o chello
       $ du -h chello
       12K  chello

But the Nim version has far more "stuff" in its executable.
There's a whole Nim standard library in there!  Check out the
nimcache/ directory which was created for us:

       $ du -h nimcache/*
       4.0K  nimcache/hello.c
       4.0K  nimcache/hello.json
       4.0K  nimcache/hello.o
       148K  nimcache/stdlib_system.c
       52K   nimcache/stdlib_system.o

Yeah, that's 148Kb of *stuff* in Nim's included stdlib_system.c.
Feel free to read it, but it's largely machine-generated.  Same
goes for the (much) smaller hello.c from our program.  A search
for the string "gophers" reveals this delightful line:

       $ grep gophers nimcache/hello.c
       STRING_LITERAL(TM_xLHv575t3PG1lB5wK05Xqg_3, "Hello my little gophers!", 24);

As your code gets bigger, the Nim standard library does not.  It's
a fixed, statically-compiled cost, and therefore easy to bear.

The wonderful thing is that even as you add more things from Nim
libraries, they're also statically compiled into your executable,
so it remains just as portable as the equivalent C program.

Since we have them, here's some other comparisons between hello
and chello (our 'C' hello).

Nim hello:

       $ file hello
       hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),
       dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, not stripped

       $ ldd hello
       linux-vdso.so.1 (0x00007fffc29db000)
       libdl.so.2 => /lib64/libdl.so.2 (0x00007f523e713000)
       libc.so.6 => /lib64/libc.so.6 (0x00007f523e34a000)
       /lib64/ld-linux-x86-64.so.2 (0x0000558719ec9000)

C hello:

       $ file chello
       chello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),
       dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, not stripped

       $ ldd chello
       linux-vdso.so.1 (0x00007ffe4a90e000)
       libc.so.6 => /lib64/libc.so.6 (0x00007f37efbea000)
       /lib64/ld-linux-x86-64.so.2 (0x000055bac2c1e000)

So the only difference between the executables (besides size) is
that Nim dynamically links to `libdl` (for dynamic library
functions) from the standard C library.

*The point is, Nim writes C so you don't have to!*

It can also write JavaScript, C++, and Objective-C.


A quick tour of Nim
------------------------------------------------------------

We "ate our vegetables" by diving deep into that mundane Hello
World.  Now we can enjoy ourselves.

There is (for now) a really fun "Easter egg" built into the Nim
tool:

       $ nim secret
       Hint: used config file '/home/...' [Conf]
       Hint: system [Processing]
       Hint: stdin [Processing]
       >>> 1+1
       2
       >>> var foo = "A string"
       >>> echo foo
       A string
       >>>

Wait!?  Nim has a REPL*?  Yeah, Nim can run Nim code at compile
time as well as generate output source and this "secret" feature
lets you treat Nim as an interpreter.  (There are some things that
aren't available in the secret REPL, but nothing we need yet.)

*REPL stands for "Read-Eval-Print Loop" and gives programmers
immediate feedback for each statement.  They're incredibly handy
for learning a new language and just trying things out.

So continuing on, we can now use Nim as a calculator:

       >>> (3+3) * 10
       60

Here's a string with escaped (backslashed) quotes:

       >>> "hello \"world\""
       hello "world"

Nim has "raw" string literals:

       >>> r"Raw strings where \ backslashes mean nothing!"
       Raw strings where \ backslashes mean nothing!

And "long" string literals:

       >>> """Long string literals which
       ... can even include newlines and "quotes"!
       ... And slashes \\\ ///"""
       Long string literals which
       can even include newlines and "quotes"!
       And slashes \\\ ///

Variable symbols are declared with an explicit type:

       >>> var x: int
       >>> x = 5
       >>> x
       5

Or they can be be inferred at compile time:

       >>> var x = 5
       >>> x
       5

There are also constant symbols which must be assigned at compile
time and 'let' symbols who can be assigned at runtime, but whose
values must never change!

We have tuples which are structures with named fields:

       >>> var gopher = (name: "Ratfactor", location: "Underground")
       >>> gopher.name
       Ratfactor
       >>> gopher.location
       Underground

(That's a tuple literal, you can also declare a variable as having
a tuple type.)

We have arrays which cannot be resized:

       >>> var foo = ["a", "b", "c"]
       >>> foo[0]
       a
       >>> foo[0]="x"
       >>> foo
       ["x", "b", "c"]
       >>> foo[3]="z"
       stack trace: (most recent call last)
       stdin(53)
       stdin(53, 4) Error: index out of bounds
       Error: unhandled exception: index out of bounds [ERecoverableError]

And sequences, which can be resized:

       >>> var foo = @["a", "b", "c"]
       >>> foo[0]
       a
       >>> foo.add("d")
       >>> foo
       @["a", "b", "c", "d"]

To see if a value is in an array or sequence, you can use `in`:

       >>> "a" in foo
       true

For control flow, we have a very nice `case` statement which can
match multiple values, ranges, and strings.

       >>> let foo = 3
       >>> case foo:
       ...   of 1,2: echo "Too low!"
       ...   of 3: echo "Just right!"
       ...   else: echo "Too high!"
       ...
       Just right!

Case also evaluates as an expression, so you can do things like
this:

       >>> echo case foo:
       ...   of 1 .. 3: "Okay"
       ...   else: "Not okay"
       ...
       Okay

If statements are what you'd expect, just remember that Nim has an
`elif` statement for "else if":

       >>> if foo == 2:
       ...   echo "argh!"
       ... elif foo == 3:
       ...   echo "barg!"
       ... else:
       ...   echo "narg!"
       ...
       barg!

While statements are pretty standard:

       >>> var foo = 5
       >>> while foo > 0:
       ...   echo "Hey gophers!"
       ...   dec foo
       ...
       Hey gophers!
       Hey gophers!
       Hey gophers!
       Hey gophers!
       Hey gophers!

The for loop is quite nice for iterating using the 'in' keyword:

       var foo = ["Nim","Is","Cool"]
       >>> for word in foo:
       ...   echo word
       ...
       Nim
       Is
       Cool

It will also fill an index variable for you:

       >>> for idx, word in foo:
       ...   echo idx, word
       ...
       0Nim
       1Is
       2Cool

Okay, let's end this little tour with procedures.  Here's a silly
one that adds 1 to a number:

       addOne(num: int): int =
       ...   return num+1
       ...
       >>> addOne 7
       8

One of my favorite things about Nim procs (and something I've
wanted in every other language since I learned about it in Nim) is
the `result` variable:

       >>> proc addOneSometimes(num: int): int =
       ...   result = num + 1
       ...   if num == 3:
       ...     result = 17
       ...
       >>> addOneSometimes 1
       2
       >>> addOneSometimes 2
       3
       >>> addOneSometimes 3
       17

See how that works?  The variable `result` is implicitly declared
for you using the return type of your function.  I especially love
using `result` when the function requires me to work in stages,
creating temporary variable to store possible return values.  (I
can't think of an exaple off the top of my head just now, but it
happens often enough.)


------------------------------------------------------------

Okay, that's quite enough of a taste for now.

It's a very pleasant language to read and write and it supports a
lot of "modern" conveniences.  It has a "script" feel but compiles
to fast, native binaries.

I think I'll dig into Nim's type system next time.

Until then, happy hacking!