Nim Programming Language 0.11.0 released
nim-lang.org
nim-lang.org
After using Python professional (and managing a team of developers) for a few years, whitespace is a great way to keep things readable as more people touch code.
Whitespace isn't the only way to do it, of course. Go's "gofmt" is a good example of a different way to enforce code layout. [1]
People that complain about whitespace haven't used Python much, it's a dead giveaway.
And yet, the benefits are enormous, I've probably skipped typing a hundred thousand redundant braces since then, and ended up with more readable code.
If this happens again it deserves its own blog post describing the situation carefully!
IDE's handle both scenarios. My IDE largely remembers how far in I should be indented and if I use a brace it closes it for me.
If I program in a plain text-editor like Notepad - I still don't mess up my indenting any more than I forget to close a brace or parenthesis. It's a silly argument formed from speculation and "but its possible!" rather than reality.
Quite the opposite. Whitespace for me is a godsend - all code is formatted the same way and I rarely if ever have to deal with whitespace related errors. In fact I don't even know what whitespace related errors are - you'd have to explain what you mean.
On the other hand, when programming in JavaScript I seem to spend all my time trying to work out issues related to brackets and code structure - very very annoying and silly - can't computers work brackets out, do we need to tell them what the code structure is?
Python: white space is a great way to keep things readable as more people touch the code.
Perl / PHP / etc: braced blocks is a great way to keep things readable if the formatting breaks.
Python: People can get lost in braces.
Perl et al: People can forget to tab.
etcPersonally I prefer braces, but I've worked with whitespaces and verbose ALGOL-style blocks too. At the end of the day, all these arguments are just theoretical because most people manage just fine 99% of the time.
True. That said, my main point is that standardizing on how to layout code is very helpful, either through the compile or using a preprocessor (like gofmt) is helpful, and more languages should encourage it out of the box.
The problem is when individuals decide to go off piste and do their own thing. But then those same individuals could equally be likely to bypass gofmt when writing code (which is very easily done), or to ignore company policy about tab spaces et al when writing Python code.
That said, I've learned to love gofmt for the laziness it brings. Text editors that auto-close brackets and such like can often be more of an intrusion than an asset. Where as gofmt only jumps in after the document is saved and the syntax is parsed for errors. So sometimes I find myself just throwing code down knowing that gofmt will tidy it up for me as I go. Which I'd be the first to admit is a pretty bad habit to get into hehehe
I was just commenting on how non-intrusive gofmt is when compared to the other tools that alter code while your programming.
I appreciate you wanting to be specific about their intended purpose but, frankly, you're stating the obvious. Anyone who's spent more than 5 minutes inside an IDE (or even basic programming-geared text editor) will already be well versed on the subject. You don't need to be an IDE developer to understand this (and to be honest, what developer hasn't written their own programming environment at some point in their lives anyway? :p )
In any case, I was just making a light-hearted passing comment; hence the "hehehe" at the end of it. It didn't really add anything to my original post and certainly wasn't meant to picked apart in this level of detail.
The only problem with white space languages are multi line lambdas.
The Ranges proposal for C++17 is great for consuming generic algorithms, but it's far behind generators for creating generic algorithms. Simple example: try to write an iterator for a tree that doesn't have parent pointers. With ranges or iterators, you must maintain a stack as a data member. With recursive generators, the variables on the stack are captured implicitly in the language. I only know a little about compilers, but it seems like a compiler with syntax tree knowledge of the entire program should be able to optimize generators into machine code that's just as good as iterators - i.e. not copying all the caller-save registers onto the stack every time a generator recurses.
I think generator syntax is truly a killer feature for any kind of serious algorithmic work. C++11 made it a lot easier to write and use generic algorithms, but writing iterators is still a big task that reluctant C++ programmers avoid. IMO, fear (and actual difficulty) of writing iterators is the main reason most people don't enjoy C++. If C++ had shipped with generators from the beginning, I think the programming language landscape would be very different today.
Nim seems to have a small community and limited promotion, so I do not feel especially hopeful that it will compete with C++ at the same level as Rust. (I am not sure if Rust will upset C++ in any significant way either!) Perhaps a well curated set of Nim/Rust/C++ comparisons could convince the C++ standards committee or the Rust team to add generator syntax.
I did a quick comparison between the up and coming compiled languages (D, Rust, Nim, Go) and C++ a week or so back. My main aim was to assess the final statically linked binary size for a simple hello world program.
Here are the results on x64 Mint:
1. C++: 1.6 MB
2. Go: 1.9 MB
3. Rust: 550 KB with dependencies (I was not able to figure out how to pass a static option to rustc)
4. D: 710 KB, same as Rust
5. Nim: 970 KB, statically linked
Nim wins, by a large margin. I did not remove any of the runtime checks by the way. Removing them actually saves 100 KB+, depending on the program. Furthermore, the Nim program was actually a naive Fibonacci number calculator, not a simple hello world, meaning that Nim should have been at a disadvantage! Amazing stuff.
We statically link by default, actually: https://gist.github.com/steveklabnik/e9e744d23a7a14edcd47
Everything but glibc. Experimental support for musl landed a few days ago.
You can take this _really_ far: http://mainisusuallyafunction.blogspot.com/2015/01/151-byte-...
I don't have the setup to try out the musl stuff, but there's instructions here: https://github.com/rust-lang/rust/pull/24777
$ cat hello.cpp
#include <cstdio>
int main(void) { puts("Hello, World!"); }
$ g++ -static hello.cpp
$ du -h a.out
857K a.out
Compiling for size (-Os -fdata-sections -ffunction-sections hello.c -Wl,--gc-sections) gives 816KWith iostream I get 1.6M
puts(u8"World is not spelled 'wørld'");... I don't like it's API either (it's a horrible abuse of operator<<, and is the exact kind of thing you tell people not to do with operator overloading...)
That said, I tend to avoid std::string as well, due to other performance issues (you tend to have a lot of copies and lots of heap allocations you don't really need). That said, I work in game development, and don't have to do much string processing, and someone in another domain might find this more painful than I do.
And re:memory, it's pretty tight on most end-user machines. By default, most laptops only come with 4-8GB of RAM. And in the cloud, memory cost is perhaps even more expensive. On ec2, a 'large' instance usually has only between 4-8GB of RAM. The default small instances have only 2GB of RAM, which doesn't give much room left over for applications after you account for the OS and various utilities.
It can definitely add up.
0) As programs are expected to do more or run faster, they are required to use more computational power and memory. As examples, this is especially true of 3D Rendering or Video Editing software.
1) Because of the price and abundance of hardware - many programmers have stopped caring about nearly all optimization unless it brings their program to a halt. Requiring more computational power and memory to run many of these programs.
To many people $50-$60 for 8GB of 2x4GB DDR3 RAM isn't a lot of money. It's about as much as a date to the movies and cheaper than going out to dinner with the family.
We could focus on musl support in order to get true static linking, but there are much higher priority things like 1.0 stability, optimizations, and compiler performance (though we'd take a patch of course). The fact is that pure static linking is rarely done anymore, at least on mainstream desktop/mobile/server systems, so it's not on anybody's critical path.
Dynamically linking to glibc does not really provide "better compile-time optimizations". LLVM already understands the libc functions that really benefit from inlining, like memcpy.
edit: tense clarification
970 KB > 710 KB.
You can argue whether or not the dependencies should be included in an executables size, but I think the comment is trying to show that Nim can create a complete program (with no dependencies) with less space.
I hope to see version 1.0 soon. Keep up the good work!
Now all we need is a cross-platform GUI library that uses native widgets on each platform, such as a set of bindings for wxWidgets or (better yet) a translation of SWT from Java to Nim, and we'd have an excellent foundation for self-contained, cross-platform desktop apps. Yes, I know desktop apps aren't trendy (at least on platforms other than Mac), but they're still important.
I agree that it's a problem with grep, there's a nimgrep tool that you could use instead, but I don't.
The nice part about it is that you can use a consitent naming convention even when using external libraries.
My idea to solve the problems this causes without removing it would be a gofmt like tool as described here: https://github.com/Araq/Nim/wiki/GSoC-2015-Ideas#nimfmt-auto...
I think a slightly nicer (in the sense that it would catch more typos) way to do it might be to specify the naming convention at the top of the source file, and have the compiler enforce that naming convention within the file.
Maybe also have a way to set it in the build system, so it could enforce it for the whole project. That way your project is consistent, but the libraries you rely on don't have to match your conventions.
If I ever design a language I might have to remember that.
That's the entire point of gofmt. Like many of Go's design choices, it's there to subtly (or not so subtly) encourage doing things in one consistent way.
http://nim-lang.org/0.11.0/manual.html#lexical-analysis-iden...
This just killed my excitement for this language. That's really surprising to me that they would choose to enforce indentation Python-style, but would then allow this kind ambiguity in naming.
grep and emacs isearch-forward are such great tools for quick code searching and this will break them. I guess text search (and replace) tools could grow nim-mode options, maybe. I don't know, can someone convince me this is a good idea?
In my opinion it should be an error or at least a warning.
I'm not a big fan of nim's behaviour, but I don't think it's any worse than what other languages do.
Them being the same is worse. I agree that both are pretty darn bad, but it's also pretty clear to me which is worse.
Having them be the same, by far.
Having names with certain similarity be prohibited in the same scope is a bit excessively controlling but sensible. Allowing them but treating them as equivalent is just plain bad. If I named them differently then either: (1) I made a mistake, or (2) I intended them to be different.
That's the same as saying "`a` and `A` should be the same variable", i.e. complete case insensitivity.
> It's a pretty bad code smell either way.
Very much so. We shouldn't encourage it.
I use same names with different styles to denote scope. I will be hosed then.
That would be a pretty hard sell. Any convenience argument falls flat to me.
If you're using Emacs to begin with... well there is little reason to complain about the default behaviour of functions and keybindings.
You also have to read others' code, as well...
Try having a variable signon and a variable signOn. Or two functions with the same type signature, one called signon() and one signOn() (for example, imported from two different modules).
Yes, let's generalize - reading the blog and the comments, this "case sensitivity ruins productivity"-problem seems to only be a problem in scripting languages.
I would see
use foo::signon;
use bar::signOn;
clearly, if I have to import this in my own file I should be VERY careful about those two functionsor rather, I'd import from foo, but I'd use the namespaced version from bar
so `signon` would be `foo::signon`, but `bar::signOn` would be written out fully to avoid this kind of clash
Here's how you declare a binding:
let signOn;
let signon;
I seriously hope you don't declare both of these in the same scopeThe feature is not meant to encourage developers to use "my_cat" and "myCat" randomly within the same file.
Treating them as the same creates more danger -- the danger that the programmer intended them to be different, but the language treated them the same -- rather than mitigating danger.
I'm sure it's not, but it isn't doing a thing to discourage that. We stopped using case insensitive file systems a long time ago, didn't we?
Windows didn't.
But even Windows doesn't use an underscore-insensitive file system.
And its worse, especially when the variables aren't "foo_bar" and "foo_Bar", but, say, "cycle_often" and "cycle_of_ten".
Such an error is caught by the compiler.
const cycle_often = 10
var cycle_of_ten : float
ambig.nim:2:5: Error: redefinition of 'cycle_of_ten'THIS_IS_A_CONSTANT SomeClass someVariable someMethod()
if line =~ rx"http[s]://(.+)":
var url = matches[0]
...
where rx is defined as: # partial regular expressions as in Perl
template rx * (arg: expr): expr =
re(r".*?" & arg & ".*")
Another macro instance: template repeat * (body: stmt): stmt {.immediate.} =
block:
while (true):
body
template until * (cond: expr): stmt {.immediate.} =
block:
if cond:
break
# Sample:
#
# var i=0
# repeat:
# echo i
# i += 1
# until i==7
Nice, isn't it?I wonder whether they will stay committed to that. Pretty much all developers of other "living" languages I know who do not have strong industry support and thus pressure not to break working code introduce breaking changes all the time e.g. see D.
Nim could distinguish itself from the crowd with such a commitment to stability. However I expect overwhelming pressure from the enthusiast crowd which currently utterly dominates among Nim users to introduce breaking changes even post-1.0. And with little pressure from the other direction..
It's a selling point for the language - a green light for companies to use the language in production environments, and when they update things they don't want their existing software to break.
Currently it seems that we distinguish between hard-real-time, soft-real-time and no-real-time, but probably we should just have hard realtime everywhere and if we do not care, we just give big numbers. I mean even if you are not hard realtime you're are probably expecting results before the heat death of the universe, so maybe it would be a nice idea to NOT abstract time in programming languages but give the possibility to the programmer to annotate timing constraints in e.g. function's definitions? E.g. such information could be crucial to the GC.
Maybe we could build such a mechanism into a fancy type theory?
If we increase the deadline to be 200ms, then even under light load, the response time of some requests can exceed 100ms. That's not what you want.
What I mean is, that you always have hard real-time in the end. E.g. you could make your network service with a hard real-time limit of 100ms. On light load you serve all requests as you mentioned with ease, if load gets heavier you hit an uncommon situation (by definition, otherwise you would not be satisfied with soft-realtime) and everything changes, as you hit a rare situation. You could do different things, e.g. in case of a news page just serve plain text and get rid of images and while you are doing that you also change to a different GC that only guarantees 500ms.
https://en.wikipedia.org/wiki/Real-time_computing#Criteria_f...
Hard – missing a deadline is a total system failure.
Firm – infrequent deadline misses are tolerable, but may degrade the system's quality of service. The usefulness of a result is zero after its deadline.
Soft – the usefulness of a result degrades after its deadline, thereby degrading the system's quality of service.Python will always be "good enough".
What's the justification for this change?