1. 28
  1. 14

    I liked Nim when I played with it but I haven’t used it for data processing specifically. Often in Python you lean a lot on libraries (numpy, and more recently pandas) to do things like read CSV files in weird formats, screw around with big matrices, etc. I don’t know if Nim has any of those.

    What I do know is that Nim’s greatest achievement is convincing people that it’s a compiled Python, and not a 21st-century Pascal dressed up to look like Python. It takes a lot of inspiration from Pascal-family languages, and I mean that as a complement.

    1. 6

      Nim’s greatest achievement is convincing people that it’s a compiled Python, and not a 21st-century Pascal dressed up to look like Python.

      Interesting, I too look very favorably on the Pascal family for having gotten most of the things right.

      1. 3

        I used Pascal a lot in the 80s — long enough to get sick of the lack of a return statement — and also Mesa (one of Wirth’s lesser known languages) at an internship at Xerox; but I’m not familiar with other languages in the family.

        Nim doesn’t remind me that much of Pascal, beyond simple syntactic things. What would you say are the deeper features inspired by it?

      2. 6

        I have been on the lookout for an indentation based language to replace Python for some time now as an introductory language to teach students. Python has too many warts (bad scoping, bad implementation of default parameters, not well-thought-out distinction between statements and expressions, comprehensions are a language within the language that makes student’s life difficult, and so on.). Is Nim the best at this point in this space? Am I missing warts in Nim that makes the grass greener on the other side? Anyone who has experience with both Nim and Python, can you tell me what the trade-offs are?

        1. 9

          I am uncomfortable with statements like (from this article) “if you know Python, you’re 90% of the way to knowing Nim.” The two languages are not IMO as similar as that. It’s sort of like saying “if you know Java, you’re 90% of the way to knowing C++.” Yes, there is a surface level syntactic similarity, but it’s not nearly as deep as with Crystal and Ruby. Nim is strongly+statically typed, doesn’t have list comprehensions, doesn’t capitalize True, passes by value not reference, has very different OOP, etc.

          That said, there’s definitely evidence that Nim has a smooth learning curve for Pythonistas! This isn’t the first article like this I’ve read. Just don’t assume that whatever works in Python will work in Nim — you don’t want to be like one of those American tourists who’s sure the locals will understand him if he just talks louder and slower :)

          So yes, Nim is excellent. It’s quite easy to learn, for a high performance compiles-to-machine-code language; definitely easier than C, C++ or Rust. (Comparable to Go, but for various reasons I prefer Nim.) When programming in it I frequently forget I’m not using a scripting language!

          1. 2

            Thank you for your perspective. Much appreciated.

            1. 1

              passes by value not reference

              The terminology here is very muddied by C, so forgive me if this sounds obvious, but do you mean that if you pass a data structure from one function to another in Nim, it will create a copy of that data structure instead of just passing the original? That seems like a really odd default for a modern language to have.

              1. 4

                At the language level, it’s passing the value not a reference. Under the hood it’s passing a pointer, so this isn’t expensive, but Nim treats function arguments as immutable, so it’s still by-value semantically: if I pass an array or object to a function, it can’t modify it.

                Obviously you don’t always want that. There is a sort-of-kludgey openarray type that exists as a parameter type for passing arrays by reference. For objects, you can declare a type as ref which makes it a reference to an object; passing such a type is passing the object by reference. This is very common since ref is also how you get dynamic allocation (with GC or more recently ref-counting.) It’s just like the distinction in C between Foo and *Foo, only it’s a safe managed pointer.

                This works well in practice (modulo some annoyance with openarray which I probably noticed more than most because I was implementing some low-level functionality in a library) … but this is going to be all new, important info to a Python programmer. I’ve seen this cause frustration when someone approaches Nim as though it were AOT-compiled Python, and then starts either complaining or asking very confused questions on the Nim forum.

                I recommend reading the tutorial/intro on the Nim site. It’s well written and by the end you’ll know most of the language. (Even the last part is optional unless you’re curious about fancy stuff like macros.)

                (Disclaimer: fate has kept me away from Nim for about 6 months, so I may have made some dumb mistakes in my explanation.)

                1. 4

                  Gotcha; I see. I wonder if it’d be clearer if they just emphasized the immutability. Framing it in terms of “by value” opens up a big can of worms around inefficient copying. But if it’s just the other function that’s prevented from modifying it, then the guarantee of immutability isn’t quite there. I guess none of the widely-understood terminology from other languages covers this particular situation, so some new terminology would be helpful.

            2. 5

              Python has too many warts (bad scoping, bad implementation of default parameters

              I don’t want to sound like python fanboy, but those reasons are very weak. Why do you need to explore the corner cases of scoping? Just stick to a couple of basic styles. Relyokg on many scoping rules is a bad idea anyways. Why do you need default parameters at all. Many languages have no support for default parameters and do fine. Just don’t use them if you think their implementation is bad.

              Less is more. I sometimes flirt with the idea of building a minimal indendtation based language with just a handful of primitives. Just as a proof of concept of the practicallity os something very simpl and minimal.

              1. 7

                At least for python and me, it’s less a matter of exploring the corner cases in the scoping rules and more a matter of tripping over them involuntarily.

                I only know three languages that don’t do lexical scoping at this point:

                1. Emacs lisp, which does dynamic scoping by default for backwards compatibility but offers lexical scoping as am option and strongly recommends lexical scoping for new code.

                2. Bash, which does dynamic scoping but kind of doesn’t claim to be a real programming language. (This is wrong but you know what I mean.)

                3. Python, which does neither dynamic nor lexical scoping, very much does claim to be a real programming language, and has advocates defending its weird scoping rules.

                I mean, access to variables in the enclosing scope has copy on write semantics. Wtf, python?

                (Three guesses who started learning python recently after writing a lexically scoped language for many years. Thank you for indulging me.)

                1. 4

                  It is weirder than copy on write. Not tested because I’m on my iPad, but given this:

                  x = 1
                  def f(cond):
                     if cond:
                        x
                     x = 2
                  

                  f(false) does nothing, but f(true) will thrown an undefined variable exception.

                  1. 4

                    I think you need nonlocal x but I don’t quite get why this is weird/nonlexical.

                    It has lexical scoping but requires you mark variables you intend to modify locally with ‘nonlocal’ or ‘global’ as a speed bump on the way to accidental aliasing. I don’t think I’d call puthon “not lexically scoped”

                    1. 3

                      Have you tried declaring a variable inside an if?

                      if True:
                          X = 1
                      print(X)
                      
                      1. 1

                        Yeah, if doesn’t introduce scope. Nonlexical scope doesn’t IMO mean “there exist lexical constructs that don’t introduce scope”, it is more “there exist scopes that don’t match any lexical constructs”

                        1. 2

                          I just learned the idea of variable hoisting thanks to this conversation. So the bizarre behavior with carlmjohnson’s example can be understood as the later assignment declaring a new local variable that comes into scope at the start of the function. Because python does block scope instead of expression scope.

                          I guess I’ve been misusing “lexical scope” to mean expression-level lexical scope.

                          I still find the idea of block scope deeply unintuitive but at least I can predict it’s behavior now. So, thanks!

                          1. 1

                            Yeah I’m not a huge fan either tbh, but I guess I’ve never thought of it as weird cause JavaScript has similar behavior.

                      2. 2

                        I agree. This is more of a quirk due to python not having explicit variable declaration syntax.

                        1. 2

                          It’s multiple weird things. It’s weird that Python has† no explicit local variable declarations, and it’s weird that scoping is per function instead of per block, and it’s weird that assignments are hoisted to the top of a function.

                          † Had? Not sure how type declaration make this more complicated than when I learned it in Python 2.5. The thing with Python is it only gets more complicated. :-)

                          Different weird thing: nonlocal won’t work here, because nonlocal only applies to functions within functions, and top level variables have to be referred to as global.

                    2. 3

                      JavaScript didn’t have it it either until the recent introduction of declaration keywords. It only had global and function (not block) scope. It’s much trickier.

                      But I am puzzled with why/how people stumble up on scoping problems. It doesn’t ever happen to me. Why do people feel the urge of accessing a symbol on a block outside the one when it was created? If you just don’t do it, you will mover have a problem, on any language.

                      1. 1

                        For me it’s all about closures. I’m used to using first class functions and closures where I suspect an object and instance variables would be more pythonic.

                        But if you’re used to expression level lexical scope, then it feels very natural to write functions with free variables and expect them to close over the thing with the same name (gestures upward) over there.

                        I’m curious, do you use any languages with expression level scope? You’re not the first python person I’ve met who thinks pythons scope rules make sense, and it confuses me as much as my confusion seems to confuse you.

                        1. 2

                          I don’t need to remember complicated scoping rules because I don’t ever use a symbol in a block higher up in the tree than the one it is defined in. Nor do I understand the need to re-assign variables, let alone re-using their names. (Talking about python now). Which languages qualify having expression level scope? Is that the same as block scope? So… Java, modern JavaScript, c#, etc?

                          I am confused. What problems does python pose when using closures? How is it different than other languages in that respect?

                          1. 1

                            I use closures in python code all the time. I just tend not to mutate the free variable. If you do that, then you don’t need to reference the free variable as global or nonlocal. If I was mutating the state then I might switch over to an object.

                    3. 5

                      Nim is pretty strongly typed, that is certainly different from Python. I’m currently translating something with Python and Typescript implementations, and I’m mostly reading the Typescript because the typing makes it easier to understand. With Nim you might spend time working on typing that you wouldn’t do for Python (or not, Nim is not object oriented), but its worth it for later readability.

                      1. 4

                        Nim is less OO than Python, but more so than Go or Rust. To me the acid test is “can you inherit both methods and data”, and Nim passes.

                        Interestingly you can choose to write in OO or functional style, and get the same results, since foo(bar, 3, 4) is equivalent to foo.bar(3, 4).

                        IIRC, Nim even has multimethods, but I think they’re deprecated.

                        1. 2

                          what? don’t you mean foo(bar, 3, 4) and bar.foo(3, 4)? AFAIK the last token before a parenthesis is always invoked as a function.

                          1. 1

                            Oops, you’re right!

                          2. 1

                            what? don’t you mean foo(bar, 3, 4) and bar.foo(3, 4)? AFAIK the last token before a parenthesis is always invoked as a function.

                        2. 3

                          Latest release of Scala 3 is trying to be more appealing to Python developers with this: https://medium.com/scala-3/scala-3-new-but-optional-syntax-855b48a4ca76

                          So I guess could make it an option.

                          1. 2

                            Thanks!, this certainly looks interesting. Would it make an introductory language, though? By which I mean that I want to explain a small subset of the language to the pupil, and that restricted language should be sufficient to achieve fairly reasonable tasks in the language. The student should then be able to pick up the advanced concepts in the language by self exploration (and those implementations should be wart free. For example, I do not want to explain again why one shouldn’t use an array as a default parameter value in Python).

                            1. 2

                              There is no such thing as a programming language that is “wart free”, and while initially you want to present any language as not having difficulties or weirdness, in the long run you do need to introduce this to the student otherwise they will not be prepared for “warts” in other languages.

                          2. 1

                            Depending on what you’re trying to teach, Elm does fit your description of an introductory language for teaching students that uses indentation. I know there’s a school that uses Elm for teaching kids how to make games, so it definitely has a presedence for being used in education too. Though, of you’re looking to teach things like file IO, http servers, or other back end specific things then it’s probably a poor choice.