Settings

Theme

Is Nim unsafe?

github.com

47 points by Pharohbot 11 years ago · 40 comments

Reader

gjm11 11 years ago

The same FAQ has a question "Why is it case/style insensitive?" and an answer giving four reasons, none of which supports "style insensitivity" in any way.

"Style insensitivity" means that in Nim the following identifiers are treated as equivalent: nOpenFiles, no_pen_files, Nope_NFiles. Insertion and removal of underscores, as well as case, is ignored. I hope you weren't expecting to find anything in your codebase using grep.

It seems a reasonable guess that, oh, maybe 99% of people wondering why Nim is "case/style insensitive" are more worried about "style insensitivity" than about mere "case insensitivity". (The other 1% haven't yet learned that Nim is "style insensitive".)

  • SamReidHughes 11 years ago

    Nim also has an experimental feature for a+b * c to parse as (a + b) * c, and for a -b to get parsed as a(-b), because of the whitespace. The documentation itself gives an example of echo (a,b) getting parsed as echo((a,b)).

    Nim also allows passing by non-const reference without any indication of such at the call site. Also it has semantic indentation, which is cute and clean-looking but more effort to safely edit than the popular alternatives.

    Everything I see in Nim is designed around being clever. Even in the documentation they have clever extensions to BNF syntax that save such precious characters.

    • dom96 11 years ago

      > and for a -b to get parsed as a(-b)

      It almost looks like you're suggesting that `a -b` will be parsed as a*(-b). Let me just clarify, that is not the case. With the 'strongSpaces' feature, `a -b` results in a compile-time error. Without that feature, it works as expected (`a-b`).

      > Nim also allows passing by non-const reference without any indication of such at the call site.

      Why is this a problem?

      > Also it has semantic indentation, which is cute and clean-looking but more effort to safely edit than the popular alternatives.

      Could you give an example of how exactly semantic indentation makes the language less safe? I have been using Nim (and Python) for years and have not found this to be the case.

      • SamReidHughes 11 years ago

        > With the 'strongSpaces' feature, `a -b` results in a compile-time error.

        Unless there's a function named a? It doesn't matter, you're cherry-picking your response.

        > Why is this a problem?

        Go ahead and tell us what benefits and detriments you're already aware of, having thought about the question, and I'll tell you if you've missed anything. e: I recommend looking at why C++ has non-const reference parameters and how C# handles the problem and considering whether the benefits of this feature could be had without its detriments.

        > Could you give an example of how exactly semantic indentation makes the language less safe?

        I said that it was more effort to safely edit. Your question drops the specificity that we're talking about making edits to the code, and also that we're talking about an effort/safety trade-off. Also, it's specifically the kind of semantic indentation found in Python, not (to such a degree) the kind typically found in Haskell. It's easy to find examples: Almost every edit that moves code around takes higher cognitive load than the equivalent done in a language where blocks are explicitly delimited.

        • beagle3 11 years ago

          > Also, it's specifically the kind of semantic indentation found in Python, not (to such a degree) the kind typically found in Haskell. It's easy to find examples: Almost every edit that moves code around takes higher cognitive load than the equivalent done in a language where blocks are explicitly delimited.

          I disagree. Moving code around in Java or C++, pure brace languages, has at least as much cognitive load as Python does, and as most languages do. Are the variables referenced defined in the new scope? do break/continue still function as expected? Are exceptions properly handled and propogated?

          The indentation is usually the least of one's worries when moving code around; and at the very least, Python (and I assume Nim, though I hardly have any experience with it) guarantees that the visual and logical code hierarchies match; The fact that they might not is a constant cognitive load in curly brace languages (and a source of bugs if you ignore it).

          • rwallace 11 years ago

            > guarantees that the visual and logical code hierarchies match; The fact that they might not is a constant cognitive load in curly brace languages (and a source of bugs if you ignore it).

            Well it's a cognitive load if you deal with it manually and the source of bugs if you ignore it, but if you use an automatic indenting program, that solves it once and for all, along with a few other problems.

        • rbehrends 11 years ago

          > Go ahead and tell us what benefits and detriments you're already aware of, having thought about the question, and I'll tell you if you've missed anything.

          Just a second, let me put on my Eiffel/Ada hat for this question.

          The contract between a caller and callee is a potentially complex thing; it specifies not only which arguments can be mutated (if at all), but also how their abstract state before and after relate to each other or what other visible side effects may be produced. I refer you to the Eiffel and Ada language specifications for possible ways to specify such contracts, invariants, and pre- and postconditions. Common convention is that functions that return a value do not have visible side-effects at all (the Ada '83 rationale specifically found limitations that disallowed all side effects at the language level too limiting). See: Command-query separation. Adhering to CQS, queries do not mutate arguments, and commands are expected to; hence, you need to know the specific contracts for commands and how they affect their arguments, but can rely on query arguments not changing.

          More generally, mutability of arguments is just one aspect of a contract; importantly, in order to be able to optimize or change the internal representation of an object, we may desire the option of having mutability of the concrete state even where the abstract state remains immutable (e.g. splay trees or self-organizing lists, arguments that have internal caches, etc.). Languages that force a knowledge of immutability of the concrete state at the call site can thus violate principles of modularity in that they limit how the implementation may change.

          Declarations of immutability of only the location of the variable at the call site (as with out/ref parameters in C#) are of even more limited value; while they guard against some clerical errors, it is not clear that this protection justifies the additional syntactic footprint.

          Back to Nim: The simple solution to ensure that a variable is not being modified by the callee, if you desire that, is to use a "let" rather than a "var" declaration. In fact, using "let" for values that you do not intend to change further and only use "var" variables where they are expected to be mutated throughout a procedure is already idiomatic Nim [1] and should guard against the majority of related clerical errors of this kind.

          [1] https://github.com/Araq/Nim/wiki/Style-Guide-for-Nim-Code

      • deathanatos 11 years ago

        >> Nim also allows passing by non-const reference without any indication of such at the call site.

        > Why is this a problem?

        I have no experience with Nim. In C++, I've heard people argue that this is poor language design because at the call site, you cannot see that the variable is potentially being mutated; making it explicit that a mutable reference is being passed allows the reader of the call site to understand what mutations might occur without needing to know the signature of every function involved.

        I've been learning Rust, which requires notation at the call site that you're passing a mutable reference, and thus far, I quite prefer it; I had been concerned that it might be "too much typing", but it's proven to be rare and unobtrusive.

        • tomjakubowski 11 years ago

          > I've been learning Rust, which requires notation at the call site that you're passing a mutable reference, and thus far, I quite prefer it; I had been concerned that it might be "too much typing", but it's proven to be rare and unobtrusive.

          I'm so glad Rust made this design choice, because it means that a programmer can see very quickly where borrows are happening and understand where and where not new borrows could be inserted.

          Take this example:

              struct Widget {
                  name: String
              }
              
              fn jabberwock_the_widget(w: &mut Widget) {
                  w.name.push_str(" (modified)");
              }
              
              fn main() {
                  let jimbo = Widget { 
                      name: "Jimbo".to_string() 
                  };
                  let name = &jimbo.name;
                  jabberwock_the_widget(&mut jimbo);
              }
          
          If that call were just `jabberwock_the_widget(jimbo)`, and I didn't have `jabberwock_the_widget`'s signature at hand, I would have to run the code through a compiler to see that there is an illegal mutable borrow on jimbo. With mutable borrows written so explicitly, it's clear at the call site that it can't happen with the existing borrow on the widget's name.
          • Gankro 11 years ago

            This has pretty limited scope, though. If a function you call returns an &mut you have no way to tell thanks to type inference, and when you use that value it looks like you're passing by-value.

            Similarly if you call a method, there's no indication at all how `self` is being passed.

            • kibwen 11 years ago

                > If a function you call returns an &mut you have no way 
                > to tell
              
              Indeed, but also consider that a function can't return a `&mut` unless a `&mut` was passed in to it somehow. This means that the only way to implicitly introduce a `&mut` is via a method call like `foo.bar()` that takes `&mut self`, but also consider that it would be impossible to call the method here unless `foo` were explicitly declared as mutable to begin with. Given that mutable variables in Rust are relatively rare, these factors conspire to broadcast any mutable references quite clearly.
    • MichaelGG 11 years ago

      I didn't get the feeling it was about being clever. Nim just seems like a lot of assorted ideas, stuff you might think up while writing in C++ or something and say "it'd be nice if...". There doesn't seem to be a grand overarching theme or design; it doesn't have the elegance some languages do.

      But it seems that doesn't matter. Some people really like it, and perhaps some interesting new feature will come out of it and benefit the world.

  • rbehrends 11 years ago

    > I hope you weren't expecting to find anything in your codebase using grep.

    This is what nimgrep [1] and nimsuggest [2] are for.

    That said, it is still expected to use a consistent style within your codebase. The feature exists so that third-party libraries with a non-standard style can be used without style changes in your codebase.

    [1] http://nim-lang.org/docs/nimgrep.html

    [2] http://nim-lang.org/docs/nimsuggest.html

    • oldmanjay 11 years ago

      > That said, it is still expected to use a consistent style within your codebase.

      One of the fundamental truths I've learned over my decades of designing software for consumption by other developers is if you enable them to do the stupid thing, they will do the stupid thing. Expectations aren't really anything meaningful without enforcement.

      • beagle3 11 years ago

        > if you enable them to do the stupid thing, they will do the stupid thing

        Yes, that's generally true, but I think irrelevant to this specific question.

        Most languages do not enforce a naming convention. Yet, Java projects almost always follow the same (horrible IMHO) standard, Python program almost always follow one of two standards (underscore_lowercase for everything, or the same with CamelCase for classes), C programs follows one of 20.

        Even though Java & Go programmers can do the stupid thing in this specific instance, they generally don't, even without enforcement.

        A C/C++ project usually has a salad of coding conventions, because each of the different libraries (e.g. Qt, GSL, libc/libm) has a different one - regardless of how conformant developers are to their official style guide.

        Nim actually has an advantage in this respect: It allows you to maintain a uniform style in your codebase, even though it is expected to call out to different styled ones; though I agree a "nim --verify_style=blah_blah" would have been a useful option.

        Personally, I think that's misguided - if you're using a library, it is better to use the original style (whatever it is) so you don't have to translate back-and-forth from examples, forum discussions, etc. But to each his own.

        • mercurial 11 years ago

          There is a difference between "enforcing a style convention" and "treating different style conventions as equivalent". The first is much preferable to the second (eg, you could easily enforce lowercase_with_underscore for identifiers and CamlCase for types).

          Treating different conventions as equivalent, though, is an excellent of ending with code that looks visually different (library A can still use Capitalized_with_underscore identifiers everywhere while library B relies on lowercase everywhere, same with Bob and Alice working in the same codebase) but is the source of surprising behaviours.

          If you want to enforce style conventions, either make them mandatory at the language level or write gofmt. The Nim "solution" is one of the things that kept me from even wanting to spend two hours checking it out.

          • beagle3 11 years ago

            It's been awhile since I've used Pascal, and I'm not yet on the Nim bandwagon - but when I did use pascal (and I did for a few years), the case insensitivity was not, in fact, a source of surprising behaviours. I have no idea what you're talking about.

            It is not really confusing that you can't have both MoveFilesEx and MoveFileSex in the same scope (and that you can refer to a variable or procedure by a either name). Nim just feels like taking this a little farther - making '_' the lower case version of no character. Not at all a big jump from Pascal, and pascal is not confusing.

            In fact, experiments teaching Python to non-programmers indicated that the two biggest confusing issues were case sensitivity and integer division[0].

            > The Nim "solution" is one of the things that kept me from even wanting to spend two hours checking it out.

            That sentiment is very often expressed by people coming from C/C++/Java about Python's indentation (What? Whitespace is significant to semantics? That's awful!). And you know what? I know not of one person who actually tried Python and actually had a problem.

            [0] http://www.alice.org/ - can't find the rational description now; but I was following its development, and IIRC those were the two biggest stumbling blocks in Python (which were therefore changed in Alice)

            • mercurial 11 years ago

              > It is not really confusing that you can't have both MoveFilesEx and MoveFileSex in the same scope (and that you can refer to a variable or procedure by a either name).

              This "feature" makes it just as annoying as dealing with, eg, PHP, where the same codebase may feature NULL or null. It encourages inconsistency.

              > Nim just feels like taking this a little farther - making '_' the lower case version of no character.

              Yes, because making grep (and search functions from code editors and IDEs) useless is a fantastic idea :(

              > That sentiment is very often expressed by people coming from C/C++/Java about Python's indentation (What? Whitespace is significant to semantics? That's awful!).

              I have no issue with Python's indentation (apart from the fact that reindenting badly indented Python is not fun). That's a different problem. Whitespace is a different solution, but not particularly confusing.

              > In fact, experiments teaching Python to non-programmers indicated that the two biggest confusing issues were case sensitivity and integer division[0].

              I feel that basing the design of a language based on the use-cases of non-programmers, for something they will likely not consider an issue after two weeks, at the expense of code maintainability is a mistake.

              • beagle3 11 years ago

                > This "feature" makes it just as annoying as dealing with, eg, PHP, where the same codebase may feature NULL or null. It encourages inconsistency.

                This is not a problem in practice, after two weeks. Seriously - Pascal has fallen out of favor, but there are large pascal codebases that demonstrate this point. NIL, NiL, nIl, nil are all accepted as the null pointer. It really isn't a problem in practice, not even remotely.

                > Yes, because making grep (and search functions from code editors and IDEs) useless is a fantastic idea :(

                No, grep is not useless, though it isn't quite as useful (just as much as #define makes grep less potent in C/C++). nimgrep fills some of the void.

                > I feel that basing the design of a language based on the use-cases of non-programmers, for something they will likely not consider an issue after two weeks, at the expense of code maintainability is a mistake.

                The Alice study was just an example BASIC and Pascal (and its predecessors) were designed in the late '60s and early '70s and feature case insensitivity. Nim follows Pascal, not Alica.

                It looks like you are really upset with choices because of some theoretical objection. I have not really used Nim, but I have used both BASIC and Pascal very extensively. Case sensitivity and alternate spellings are NOT problems in practice, even though you try very hard to imagine that they are.

                • mercurial 11 years ago

                  > It looks like you are really upset with choices because of some theoretical objection. I have not really used Nim, but I have used both BASIC and Pascal very extensively. Case sensitivity and alternate spellings are NOT problems in practice, even though you try very hard to imagine that they are.

                  I'm not upset, I happen to think that these choices add complexity and promote inconsistency for little practical gain.

                  > This is not a problem in practice, after two weeks. Seriously - Pascal has fallen out of favor, but there are large pascal codebases that demonstrate this point. NIL, NiL, nIl, nil are all accepted as the null pointer. It really isn't a problem in practice, not even remotely.

                  If you want to enforce a consistent style, it's one more obstacle in your way. And considering how often projects end up with little technical lead and no enforcement of style whatsoever, I'd rather avoid that.

                  > No, grep is not useless, though it isn't quite as useful (just as much as #define makes grep less potent in C/C++).

                  It makes it useless to find an identifier, which means useless 90% of the time I want to use it on a codebase.

                  > nimgrep fills some of the void.

                  See? More complexity for no appreciable benefit. A much saner approach would be to implement a compiler warning for this kind of thing.

  • coldtea 11 years ago

    My problem with most languages is not that they are not perfect. Not perfect I can understand and work around.

    The problem is that they go out of their way to add some batshit crazy stuff like this, or cripple themselves without a reason, when they could be so much better with small and simple additions.

    Would it be so difficult for Javascript to not ever have those crazy silent coercion rules and have lexical scoping by default? It would have been a trivial compiler change back in 199x when JS was designed.

    Would it be so difficult for Golang to have a proper generics system from the start and be done with it?

    Would it be so difficult for Java to add closures 10 or so years ago?

    etc...

    • mercurial 11 years ago

      There is a difference between "not having features" (even not having features by design) and going out of your way to design an absolutely terrible solution to a problem that is, AFAIK, not used in any other language out there.

      • coldtea 11 years ago

        Yeah, this Nim thing is on the top of the crazy list...

        • Pyxl101 11 years ago

          Why is it crazy? I've wanted this feature recently in other languages. Just because someone else wrote FunctionName doesn't mean that I want to write it that way.

          Case insenstive search solves the matching problem adequately. You could also do underscore-insensitive searching.

          Why is it a problem, or crazy? It's a natural extension of being insensitive to whitespace and formatting. If you like your code to look_like_this, fine. Someone else PrefersThis. It can all interoperate. I think that's great. Leave style to each person's preference.

          • coldtea 11 years ago

            >Just because someone else wrote FunctionName doesn't mean that I want to write it that way.

            If it's different codebases, you can do that in almost any language. Have some comebases write fooBar and others write foo_bar or whatever, whatever each one prefers. It's not very good even in that case, because you'll often have to read somebody else's codebase (which is why people praise go fmt and organizations have coding guidelines), but at least there it's OK.

            But if you're talking about FunctionName and functionName and function_name in the SAME codebase (and obviously you do, since for the previous case you don't need Nim at all as I said) then that's were madness lies -- willingly impairing yourself to identify optically a symbol to be the same as another or not, because it can have 20 variations...

            I've never found this "flexibility" and bike-shedding that ensues useful in any language. I'd even prefer a fixed brace style, even it's not the one "I like". It takes a few weeks of familiarity to learn to perfectly see a different formatting style and even "like it".

            • Pyxl101 11 years ago

              If we were following this idea, then I think source would be displayed according to your preference whenever you looked at it. You'd never need to deal with multiple symbol variations. The goal would not be to use multiple styles in on code add so much as make style entirely personal.

          • mercurial 11 years ago

            > Leave style to each person's preference.

            That's fine for hobby projects. That's pretty much the last thing you want for a codebase of a significant size. I don't like C#'s style conventions, for instance, but I'd rather work with a language that has uniform style conventions I dislike than in the free-for-all you get with Nim.

            Python's "There should be one-- and preferably only one --obvious way to do it." applies to style as well.

    • coldtea 11 years ago

      Some more examples:

      C to have been designed with a proper string type...

      Size of array not part of its type for Pascal?

      Fucking named constants in CSS?

  • PopeOfNope 11 years ago

    So the largest thread here is about something off-topic (case sensitivity has nothing to do with safety in Nim) and it's something that's utterly irrelevant. Bravo.

    • vortico 11 years ago

      It's pretty typical for Hacker News to be a forum for talking about whatever.

overgard 11 years ago

> It's not hard to "fix" this issue anyway. Where "fixing" means "pretending Nim created Ansi C code in the first place" (which it doesn't).

They should probably fix the wording of this -- as an outsider I can't really parse the message conveyed here. If it doesn't create C code then why does the comparison matter? Or is it just about the "ansi" part? But then why does that matter?)

PopeOfNope 11 years ago

When I read between the lines, this section says to me: "Yes, Nim is unsafe, but there are features on the way to help it be more safe."

'not nil' sounds like a great addition to the language, but what if you wanted that to be the default behavior without having to pepper it throughout every variable in your codebase?

  • PharohbotOP 11 years ago

    Well the biggest concern for most is null pointers, so invoking not nil on the pointers would suffice for most

pyre 11 years ago

> Is Nim unsafe?

Are the binaries hosted on SourceForge?

Dewie3 11 years ago

Good old null pointer.

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection