1. 31
  1.  

  2. 4

    As someone with maybe 20 hours of experience writing Nim, I’m almost impressed and generally just a little frustrated. For a new language with no baggage it’s just ludicrously inconsistent.

    For loops:

    for x in 0..10:
      echo x
    
    # backwards ranges have a completely different syntax
    for x in countdown(10, 0):
      echo x
    

    Hash tables:

    # making a hash of 1->2 like this works fine...
    var x: TableRef[int, int] = {1: 2}.newTable()
    
    # ... but the empty table is a compiler error?!
    var x: TableRef[int, int] = {}.newTable()
    
    # this is how you're supposed to make an empty table
    var x = newTable[int, int]()
    

    Working with arrays:

    var thing = @[1, 2, 3]
    # Some functions accept a callback:
    thing.sort(compareFunc)
    
    # but some don't, such as maxIndex (and there's no max):
    thing.maxIndex()
    
    # there's also family of "It" functions that take a code template 
    thing.mapIt(it * 2)  # doubles the items in a list
    
    1. 4

      I found the fact that you can set a magic variable “result” or use the return keyword, to be pretty strange too. I’ve been reading through “mastering nim” and have run into a few other things like this that I found likewise strange and/or inconsistent. With so much to otherwise like about the language, these little oddities stand out a bit more starkly. It eventually becomes off-putting.

      1. 1

        I found the ‘result’ thing weird initially too, but after getting used to it I really like it, it feels very natural and I prefer it to the other ways of returning a value.

      2. 3

        For a new language with no baggage it’s just ludicrously inconsistent.

        Nim actually has massive amounts of baggage IMO, with the language going through multiple changes in focus over its lifetime and having quite a bit of fancy extra functionality that was never actually fleshed out.

        1. 1

          Care to elaborate?

        2. 1

          While there is a good deal of variation, much of this is from Nim starting in 2006..8 and being 14..16 years old now with consistency conventions evolving.

          While your main point is not wholly off-track, your examples rub me the wrong way. To take just the first, the countdown iterator is anti-symmetric with the countup iterator, both of which take a step size. `..` follows more natural language notation which is asymmetric. It’s only 2 lines of code to add an anti-symmetric counterpart, but your code readers may not find guessing its meaning as easy:

          iterator `-->`[T](a, b: T): T =
            for x in countdown(a, b): yield x
          for x in 10-->0: echo x
          

          So, the inconsistency you are complaining about on this line item is just one of human language abbreviation. Given the above you would also want a <-- (or maybe the above should already be that with args reversed? Often it’s a choice of what to be consistent with, in this case loop direction OR argument order).

          Anyway, this is maybe a good example for readers of how, unlike many prog.langs, Nim lets users just define their own operators. Unlike Python, there is really no penalty to just adding your own “shims” or layers on top of the stdlib to match whatever consistency you like. (Nim’s macro system could even automate that.)

          While the starting state may not be to your liking, you absolutely have the power to fix everything like the things you complained about in your local code base. (But I can understand not wanting to..some love an out of the box experience that happens to perfectly align with their own tastes while Nim Is Choice is something I often say).

          1. 4

            While the starting state may not be to your liking, you absolutely have the power to fix everything like the things you complained about in your local code base. (But I can understand not wanting to..some love an out of the box experience that happens to perfectly align with their own tastes while Nim Is Choice is something I often say).

            That’s all well and good but I don’t think that’s entirely fair here. A New user (at least this new user) doesn’t yet have the ability to go down a rabbit hole of macros and templates to fix empty table creation. I don’t think it’s unfair to say the language would be objectively better without that particular wart, so why make everyone fix it in a slightly different way?

            And this isn’t purely some “Ew, look how gross that is,” reaction but rather a very real issue of discoverability.

            Anywhoo, I agree that There’s No Accounting For Taste. Maybe if I were more invested in nim I’d be willing to tweak it to my satisfaction…but even then, I’m not super excited about an ecosystem of software where reading code is hard because everyone solves these papercuts in his or her own special way.

            1. 2

              There is no question that there are major discovery issues. Part of that is also “order of discovery” and some patience on the part of any new user of anything is required. Diversity of tutorials (of anything) is valuable partly because different people ask questions in different orders and come from various backgrounds.

              E.g., there is a consistent way to create most any empty container (Table, HashSet, seq, etc.) - just declare it, like var x: Table[int,int]. With value types the convention is an init pfx like var y = initTable[int,int](..).

              If you are coming from a more Python direction, an empty comprehension/builder style is probably a closer match (but verbose):

              import sugar, tables, sets
              let x = collect(for i in countup(0,-1): {1:2})
              echo x # gives you an empty table
              

              Change the {1:2} to whatever two types you need or {oneVal} for a HashSet or a scalar for a seq. Change the countup part to something “more real” and you get a builder notation along those lines. Verbose for “empty” compared to just a declaration, but more extendable to other construction loops.

              ref is more complex, replacing init* with new* and there are other ways using {} with maybe glitchy (open an issue?) behaviors, but not every way is glitchy or inconsistent and there are choices (somewhat consistent along some dimension).

              Anyway, I’m not claiming it’s perfect, as I tried to make clear initially, or trying to be unfair. There are less bazooka ways to go than macros unless you are clinging to “must have XYZ syntax”. I mostly think had the example code/tutorial/whatever you based your quick impressions upon been different then your complaint examples would have been very different and that “Right To Repair” is an important property. Both may be vacuous observations, but, hey, I put some more Nim examples in there for other readers. I’m really only trying to add information for thoughtful consideration. Sounds like your interest in Nim has come & gone, though which is fine. No obligation to like anything.

        3. 3

          What’s the deal with Nim? It seems like it’s in a similar category to golang, but more Pythonish than Java-ish.

          1. 9

            What’s the deal with Nim? It seems like it’s in a similar category to golang, but more Pythonish than Java-ish.

            Go’s primary use case was for writing server-side web applications at Google. Nim is more of a systems programming language. Someone was even trying to clone the xv6 kernel in it at one point, but I’m pretty sure that effort is abandoned.

            FWIW, it can also compile to JavaScript. Their forum, forum.nim-lang.org, is a single-page application written in Nim. That says a lot about how flexible it is.

            NB: I don’t program in Nim and don’t have any stake in it, but it looks cool from an outsider’s point of view.

            1. 1

              Go also has (had?) a JS target. People got excited about it for a while, but the juice wasn’t worth the squeeze as far as I can tell, and it seems like it’s mostly been forgotten about, especially since wasm support landed.

              1. 1

                Go also has (had?) a JS target. People got excited about it for a while,

                This stuff is way far away from my areas of interest, being a fervent hater of all things modern web and SPA. But their SPA did seem responsive when I last looked. And hey, from a purely technical point of view, it is clever and interesting in a way that “someone did a thing with Framework of the Week” is not. Since the Nim project is eating its own dogfood, it is also an incentive not to let that backend bitrot. It’s a testimonial for the breadth of the language. I never thought I’d be saying “Oh cool, someone made an SPA”.

                1. 1

                  I think there are two. GopherJS and WASM targets.

              2. 7

                More or less. I mentally bin it in with Java and C# and Go and D in the bucket of “reasonably fast application languages”. I consider it quite competently designed made, it has some ability to do low-level programming but it is mainly used with a GC. I don’t use it much because I’m usually either doing lower-level stuff in Rust or gluing stuff together with Python/Lua… but I wish I had more reasons to use it.

                The dark secret is its syntax is a lot closer to Pascal than Python. Shhhh, don’t tell anyone.

                1. 3

                  I personally often find Nim more convenient as a glue language than Python.

                2. 5

                  That’s basically it, sure. It’s in that Go, C++, D, Rust configuration space. Implementation/output-wise between D and C++, textually/input wise somewhere around Python with a dash of macros between Rust and Lisp (I don’t know any D to comment on its macros so sorry if I left it out).

                  It compiles to C, it has a runtime including threads and garbage collection but it’s optional (so lighter than Go’s or Java’s). It has a very Python-y syntax but I’ve seen the author of this post’s code and they use quite a lot of the macro system and it feels pretty natural so it’s hard to pin it too hard in that Python bucket but certainly at a glance it looks that way.

                  It compiles to C (or javascript, or wasm, or some other targets) and supports most of the native datatypes like pointers that make FFI somewhat straight forward so it’s pretty easy to plug in to most ecosystems.

                  It’s a small cult following for sure, there aren’t Nim folk coming into every thread telling you to rewrite your stuff in Nim

                  1. 3

                    D (deliberately) doesn’t have macros.

                    1. 2

                      Between templates, template mixins, value/alias template parameters, string mixins, CTFE, etc., metaprogramming in D is effectively as powerful as macros though right? I think a lot of people would consider string mixins + CTFE macros.

                      1. 4

                        Okay, yes. D has deliberately bad macros. :)

                        If you limit yourself to D’s built-in syntax, you can do some macro-like things. But you can’t destructure things and you can’t mess with custom syntax outside of strings.

                  2. 4

                    More like an early Rust with a wilder syntax, GC (originally, now giving way to a more static memory management system[0]) and lesser focus on thread safety.

                    [0] https://nim-lang.org/blog/2020/10/15/introduction-to-arc-orc-in-nim.html

                    1. 4

                      one of the challenges with newer (or in general less-mainstream) programming languages is that they do not have client libraries for major open source databases, stream processors, UI toolkits, and various other middleware.

                      For example I cannot find Zig, Racket, Nim client libraries for Kafka Streams. probably the same is true (to some degree for QT, may be for other major databases as well)

                      I am not saying that this is a language issue, it is just these things make enterprise adoption in the mean time more difficult for low-budget/see-if-it-works kinds of efforts.

                      1. 5

                        At least in Zig it’s first-class supported to just use the C library for it.

                        1. 2

                          Zig is another language that gets pretty rave reviews from people enjoying exploring it. I’ve yet to encounter it (or Nim, or Crystal) in real life, and I’ve barely ever seen Rust, but it’s cool that people are continuing to innovate and explore in the language area. I’m ready to let C++, Python, Java, and JavaScript slowly retire into the sunset, even though they’re all incredible languages with extremely rich ecosystems.

                        2. 3

                          Yeah, we’re pretty spoiled at this point by the plethora of options in well-established languages. 🤷‍♂️

                          But it’s pretty cool to read the excitement in this blog article on Nim. Programming should at least occasionally be fun, and it sounds like Nim helped him find that fun again. That’s something that I can appreciate.

                        3. 3

                          Go and Nim are both descendants of Pascal.

                          Go is optimized for disposable code monkeys at an advertising company. Nim is a hackers’ Pascal.

                          1. 2

                            You answered your own question. Why the question mark?

                            It is very pythonish and has the benefits of other languages you mentioned.

                            As mentioned by others, it läks a killer library/application.