Hacker News new | past | comments | ask | show | jobs | submit login
Nim Programming Language Tutorial (nim-lang.org)
136 points by j-b on Dec 29, 2014 | hide | past | web | favorite | 31 comments

I've just read through the Nim manual. On the surface, it looks as simple as Python - but it is ridiculously deep when you look at all the template, macro, term rewriters, etc.

Whether or not you can make use of it without being aware of all those things remains to be seen. I suspect the answer is mostly yes, i.e. that one can use the nim ecosystem effectively without knowing all of that. (my answer for C++ would be "mostly no" - you need to really know everything about all the dark corners of the language to use the ecosystem effectively).

I'm trying to think of a good way to implement automatic reference counting with copy-on-write, e.g. for big arrays, but haven't come up with one yet. Could a Nim veteran kindly point me in the idiomatic direction?

You cannot do these things easily currently since the assignment operator cannot be overloaded. There are ways around it, you can "fix" the broken builtin assignment with a TR macro, but since TR macros MUST not change semantics (you can disable them on a global level and code shall continue to work!) this is a bad idea.

How about doing something like:

    type refCounted[T] =
      val: T
      refs: int

    proc `v=`(var x:refCounted[T], var y:refCounted[T]) =
      if x.refs <= 0: destroy(x.val)
      x.val = y.val

    # use let so x and y cannot be assigned    
    let x, y = refCounted[string]

    # instead use x.v to assign and manage refs properly.
    x.v = y
using "let" would make sure all assignments happen through a blessed method (such as the ".v" assignment I defined above).

But to actually make it work, I would need to decrease reference on going-out-of-scope (as there a way to do that? maybe a python "with" style enter exit macro?).

And to make it easy to work with, there would need to be a way to pre- "incref" an argument before it is passed as an argument, and "decref" it after the function call return. I can probably write a macro that rewrites every function call with a refCounted arg so that it increfs() on the way in, decrefs() on the way out; or maybe have that macro on the callees instead.

Guess I'll have to try the different approaches and see what works and how efficiently.

I think that should be

    type refCounted[T] = object
      val: T
      refs: int
You could try a destructor for the object: http://nim-lang.org/manual.html#destructors

Hey I come from Python too and I just want to put out there both that Nim is a normal language - you can use it without templates or macros (you might end up using someone else's macro because some stdlib or library is implemented as one, but you don't have to write them yourself) - but also that Nim's macros are quite easy to understand!

They are just functions that take an AST tree, modify that AST or produce a new AST and return it. Its just normal Nim code working on an AST object. That's it! I wrote a small blog article about them when I first learned about them: http://blog.ldlework.com/a-cursory-look-at-meta-programming-...

I encourage you to conquer your fears!

Oh, I have no fear.

I was just comparing to C++ - where you have to understand how everything about templates and the STL works if you intend to understand the error messages produced by the compiler if you use STL. Or any library that interacts with STL containers. I heard things on this front have improved - I myself have left C++ for C a while ago.

C++ is a monstrosity. A common advice is "just pick the parts you like and stick to them". But it never works that way - as soon as you interact with 3rd party code, you have to deal with the parts THEY liked.

In comparison, my intuition says that despite all the dark corners (and if you read the entire manual, there are quite a few of them), that's not the case with Nim; Instead, if you treat it like a dialect of Python, you'll get simplicity close to Python; You'd only need to understand those dark corners if you plan to make use of them.

But I will need actual experience in Nim to find support for or against this intuition.

Hey I am just curious as to what you see as the dark corners of Nim?

Python is pretty deep too once you start digging with stuff like dir(), and various other introspection libraries.

For those wanting a quick grasp of the language, here's Nim in action:

- against Python: http://rosetta.alhur.es/compare/Python/Nimrod/#

- against Ruby: http://rosetta.alhur.es/compare/ruby/Nimrod/#

Wow, that's really cool! Never seen that before. (I implemented many of the Rosetta Code tasks in Nim)

I'm intrigued by Nim after browsing through some of the basic tutorials. Looks quite straightforward to get started. As with other languages I look at, for my use-cases it's important for me to understand0 if scientific packages are available and if there's a "scientific computing ecosystem".

I had a look through the `packages.json` [1] listing for Nimble [2] and couldn't really spot the maths packages that I'd need, e.g., numerical integration, optimization, linear algebra. Given that I can't find any of those, I'm guessing there aren't any physics packages either e.g., Newtonian mechanics, physical constants etc. Maybe maths libraries are available as ports to e.g., Eigen, NLOPT etc.?

Can anyone that is more familiar with the community comment on the potential of Nim as a language for scientific computing, in contrast to e.g., Julia?

[1] https://github.com/nim-lang/packages/blob/master/packages.js...

[2] https://github.com/nimrod-code/nimble

[3] http://eigen.tuxfamily.org/index.php?title=Main_Page

[4] http://ab-initio.mit.edu/wiki/index.php/NLopt

EDIT: Fixed typos

While I'm not super familiar with the scientific computing field, I can say that Nim needs everyone's help in building the library ecology. Right now, Nim seems to be in the wrap-all-the-things! mode. You can see that most of the available packages are bindings to existing libraries. I'm not sure this is a bad thing. And I don't think it has anything to do with anything other than Nim's currently small community.

I don't see a problem with that. Since Nim compiles to C, it's only natural for people to take advantage of existing C libraries.

I haven't dived deep enough to figure out if integration with existing C/C++/Fortran libraries works out-of-the-box, or whether you have to setup bindings a la Python. If it works out-of-the-box, my question is moot, especially if two-way interoperability with Nim code is seamless.

You have to set up bindings but it is as simple as it could possibly be to do by hand, and there is a c2nim program to help.

I certainly agree.

I'm quite surprised that Nim's strings are null-terminated, and have a length field. According to a forum article[1], it is mainly due to the C interoperability.

I understand that FFI is kinda important for system languages, but isn't it giving up too much to disallow null characters in the middle of a string? Null-terminated strings generally work well, but there have been some corner cases that made me annoyed. (Such as PHP's preg_* functions)

[1] http://forum.nimrod-lang.org/t/125

This is actually really common in C++ libraries. I even worked in one (which I know was on Windows, but I don't think was MFC; maybe OWL?) where the pointers to the String object were actually pointing directly to the null-terminated C string, and then each function in the class would start by fixing up the pointer. E.g., something like

    class StringClass {
        ...all members, and then...
        char *str;

    StringClass *StringClass::new() {
        StringClass st = new StringClass;
        return (StringClass *)st->str;

    void StringClass::someFunc() {
        StringClass *realThis = this - sizeof(StringClass) + sizeof(char *);
Evil, eh?

The good news is that, no, this does not have to prevent you having nulls in your string. The way this usually works is that all of the language's native string handling routines just use the length, so that's fine, and you're at least protected if you want to call C string handling routines instead. This can result in some unexpected behavior on the C side, but it beats the alternative.

One example is std::string as implemented in the GNU libstdc++ or whatever it's called.

Thanks for that explanation

> but isn't it giving up too much to disallow null characters in the middle of a string?

You can have null characters in the middle of strings:

  var s = "foo"
  s[1] = '\0'
  echo len(s)
  echo s[2]
Nim will always append a null character to a string for when it gets passed to a C function, but Nim's string functions work just fine with the string proper containing null characters also.

Nice tutorial pitched just right for me. Looks a lot like Iron Python iirc. I like that you can start writing old school interactive command line apps so easily -- great for starting out and in the absence of solid gui tools.

I just spent an hour going through Nim related webpages by users who write in it. This is a good find, thanks.

This is another excellent Nim resource: http://goran.krampe.se

Here is a slightly longer list of external articles I made.


Is that german quote from something other than Rammstein lyrics?

I don't see anything particulary special here. The metaprogramming features are quite weak. The macro system is very primitive. The number types seem to be lacking, too. I don't see anything about bignums, rationals, or complex numbers.

Metaprogramming: http://rosettacode.org/wiki/Metaprogramming#Nimrod

My bignum library for Nim (not well tested yet): https://github.com/def-/bigints

Complex numbers: http://nim-lang.org/complex.html

How is the macro system primitive?

>My bignum library for Nim (not well tested yet): https://github.com/def-/bigints

>Complex numbers: http://nim-lang.org/complex.html

Ah, good! Glad to be wrong about that! It would be nice if Nim's reader could handle these numeric types transparently.

>How is the macro system primitive?

Well, I like to use macros to create new syntax, which AFAICT you can't do that with Nim. I'm used to using 'define-syntax' in Scheme, and Nim's macro system seems to be far less robust than that. I guess that's the price paid for non-homoiconic syntax.

I just don't see anything new and exiciting in Nim. :(

You're really very wrong on Nim's macros. It follows Lisp's defmacro tradition instead of Scheme syntax-rules/syntax-case, but that doesn't make it any less powerful (many would argue it's demonstrably more powerful). You are also dead wrong on syntax-rules/syntax-case capabilities, or maybe on what the syntax/AST is, if you think that there's anything they can do that Nim can't. Both systems deal with AST which means they both are unable to introduce new ways of parsing the code, only transform already parsed one. In (some) Scheme and Common Lisp you get access to readtable, which is the parser, but that's really a different thing. And even in Lisps it's not that popular: Clojure and Emacs Lisp disallow this for example.

Personally I favour pattern-based macros, like the ones implemented in Dylan, Elixir or Sweet.js (to show some non-sexp-based languages with such macros); but there is nothing "wrong" with procedural macros and they are not, in any way, less robust.

You don't have to be excited by Nim, but you should try to avoid spreading lies just because you aren't. Maybe a "lie" is too strong a word, but this statement: "Nim's macro system seems to be far less robust than that" is really very wrong and I wanted to stress this fact.

The vast majority of languages outside the Lisp family have no procedural macro system - and Nim's is more powerful than that of many that do. It's difficult to imagine how that's "primitive".

Applications are open for YC Winter 2020

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact