Nim is frustrating, because to my taste it gets so many things just right while getting one particular thing so spectacularly wrong that I can't bring myself even to try it.
The one thing is its rule for when two identifiers are the same. They are compared case-insensitively, ignoring any underscores and en-dashes in either name, except that the first character must match exactly.
Consequence #1: if you have, say, something called user_sort (perhaps it's a function that sorts users) and something called use_RSort (perhaps it selects which of two sorting algorithms to use somewhere) then they will be the same identifier, with results anywhere from compilation failure to silent malfunction you don't notice until a year later.
Consequence #2: if you want to search for an identifier using some obscure tool like, say, grep or Vim or Eclipse, you're out of luck. (You can make your own tool -- call it "nimgrep", perhaps -- and extensible things like Vim and Eclipse can be given the ability to search for Nim identifiers. But their built-in facilities, like what "*" does in Vim, will do the wrong thing if you are working on Nim code.)
I have used Nim heavily for more than a year now, and it turned out to be the most productive language I have every used. Nim could be considered a modern Lisp with infix notation and Python like syntax with native C performance. The macro system is incredibly well designed and easy to handle.
I also tried Rust, and it turned out to be much more complicated and time consuming. I would use it for systems programming only. Redox is the only reason for me to learn Rust.
> The one thing is its rule for when two identifiers are the same.
First it looked strange to me as well. However there are actually good reasons to avoid case sensitivity.
http://blog.codinghorror.com/the-case-for-case-insensitivity...
As for the user_sort and use_rsort example, such semantic errors can easily be fixed by qualification (a.user_sort and b.use_rsort). Errors like the following one however are very hard to fix in case sensitive languages like C++ and probably even Rust:
module A: func smart_func (x,y) = do_this
module B: func smart_Func (x,y) = do_that
module C: func Smart_Func (x,y) = do_something_else
main: import A, B, C
smart_func (a,b)
hmm ... smart_func(a,b) doesn't work. Seems to be a typo. Which function did the author really mean?Nim would reject such a mess in general.
The developers of Nim have implemented a lot of nice features. A really silly idea however are strong spaces. No serious developer would ever use such a dangerous "feature". It should be removed from Nim.
> Errors like the following one however are very hard to
> fix in case sensitive languages like C++ and probably
> even Rust
No, in Rust the compiler will emit a style warning if you have a function whose name includes capital letters. This can be toggled off (and also elevated into a hard error), but it's always on by default.Furthermore, when you import a module Rust keeps it namespaced under the module's name, so you never need to worry about names accidentally colliding or being unsure as to where a symbol comes from. You have to use an explicit glob import to pull in all of a module's public items into the current namespace.
Furtherfurthermore, Rust warns when you import a symbol and then don't use it, so even if you name all of these functions differently and ignore the style warnings and glob-import the modules, then you'll get yet another warning when you fail to ever use two of the symbols.
Furtherfurtherfurthermore, Rust doesn't allow symbols with the same name to exist in the same namespace, so even if you went back and fixed all those functions to comply with the style warnings, you'd get compilation errors if you continued to glob-import any of the two of them.
My problem with Nim's identifier syntax rules are not about case-sensitivity. I have happily used case-sensitive languages and case-insensitive ones. My problem is with ignoring punctuation in identifiers.
> such semantic errors can easily be fixed by qualification
Once you notice that there's a problem. The trouble is, you might not.
> [three things with names like smart_func but different case]
Again, case-insensitivity is a red herring here as far as I'm concerned.
Nim supports all styles. A simple reformatter can be used to transform the code of a project into a normalized form. Such an attempt would be very dangerous in C++.
That's certainly better than having different bits of the code use different identifier styles. But it feels somehow a fragile way to do things. And I worry about encouraging bad habits in teams that don't set things up with such care.
And... can you really make a simple reformatter that will consistently do the right thing? Identifiers may make reference to things whose capitalization shouldn't be canonicalized. HTML. TeX. iPhone. DrMemory.
(Also, though this is a bit of a cheap shot: note that the three identifiers in your first paragraph are not equivalent according to Nim's rules.)
No matter what language you use, if you have two variables named myVariable and my_variable in the same scope, you've created a huge land mine in your code, and your linter should complain. Eventually somebody's going to come along and mix the two up.
The fact that nim blows up differently than every other language just makes it more obvious that this is something you just shouldn't do.
What bothers me about Nim is that (1) it makes it easier for you to call the same variable myVariable in some places and my_variable in others because that won't make compilation fail; (2) once that happens it becomes harder to find references to that variable, because the usual searching tools will miss some of them (and you will not necessarily get any indication that you've missed any); and (3) there are -- hopefully rare, I concede -- cases where what in another language would be obviously and innocuously different identifiers collide. nOpenFiles versus no_pen_files. use_rsync versus user_sync.
And what does this buy you? What is the advantage it confers? That you have the flexibility to call the same thing myVariable in some parts of your code and my_variable in others. But that's a terrible idea and making it possible is not a feature.
But that's not the situation in the example. user_sort and use_RSort are perfectly fine identifiers that no reasonable person would expect to be interpreted as the same.
#1: those variables have to be in the same scope or be procs working on the same types to be an issue.
#2: I don't use nimgrep, I just keep a consistent style across my files. When reading somebody else's code, case-insensitive search is usually enough.
I agree that "just keep a consistent style" is a good approach for single-person projects. It's harder to keep it working well as you get more people working on the code, though.
You cannot do this in C++ and Rust. There you actually have to keep a consistent style.
http://blog.codinghorror.com/the-case-for-case-insensitivity...
(I do not think your analysis is anywhere near correct in my case, even though I think Nim's identifier comparison rules are very unwise.)
Your comment is the only one I have seen making such a big issue about the variable naming.
It reminds of people who dislike Lisp only for its "ugly parentheses".
I pointed it out on the boards and the response wasn't all that great, so I decided to spend my time elsewhere.
I started picking up Rust a few weeks ago, so far I'm having fun :)
Any chance you could give me a link to the thread/issue you posted? I really want to see where we went wrong.
IIRC, the issue I posted about had to do with some of the underlying parsers. I was looking at the CSV stuff and I believe one of the types it relies on had a skipBOM function, or one of the functions would skip the BOM, or something along those lines.
But what it did was blindly increment the file pointer without checking it was looking at a BOM, or that it was at the start of the file.
To me the interface is a fairly big deal because you can't safely use the type without knowing that implementation behavior. People do stupid crap, and calling skipBOM at the wrong time will happen, even though it's obviously not intended. What I recall is recommending that it either fail noisily or turn into a noop. I'm on the side of failing noisily to help users of the class find their bugs quicker, but I can understand why someone would want it to be a noop since technically doing nothing if it's not a BOM is the right thing to do.
The response I got was the type wasn't meant to be part of the public interface for the stdlib and so wasn't an issue, and then I was pretty much ignored after that statement. I even offered to do the work myself, but pretty much got ignored.
It wasn't so much that the reaction was negative, as much as it wasn't conducive to improving the stdlib all that much. And from my perspective, one of the biggest problem nim had (at the time) is that the stdlib felt very unpolished. As if someone had written it for very specific use cases and then haven't revisited it. I guess what I'm saying is that it didn't feel like an stdlib in many places, it felt like someone who wrote some production code for very specific use cases. Which is perfectly acceptable for production code with a specific use case, but not for an stdlib (in my opinion).
The entire thing left a bad taste in my mouth and I lost a lot of trust in the quality of the stdlib and so decided to stop learning/using nim.
It's all a little fuzzy in my mind, so I may not have all the details correct but the loss of trust was definitely the result.
And who knows, perhaps I was misreading the code, I wasn't all that experienced with nim at the time. But even having someone point out it was my mistake would have felt better than simply being ignored.
It's too bad really, because I did like the language itself, which is why I had offered to try and help improve the stdlib. Nowadays I'm picking up rust for fun(putting together an NES emulator in it). I'm a little bit of a language whore :)
From looking at the thread, it seems that this is a simple case of your thread becoming lost in the noise of the forum. Please don't feel that you were ignored on purpose. I think that Araq simply forgot to reply to you, it's easy for threads to get lost like this.
It really sucks, but the fact is that Araq doesn't have enough time to answer everyone and in this case he is the only one who could have assisted you.
In all honesty I think this is quite common in forums. I'm not sure how we can improve it, short of getting full-time Nim devs who ensure that everyone on the forum gets a response. What do you think?
But that isn't even the important point. The deficiencies I outlined haven't been been revisited have they?
If not, it would seem my distrust wasn't so misplaced, and that was the bigger point.
Who knows, maybe the rest of the stdlib has had more thought put into it, but that was the first bit of the stdlib I had dove into the source for and what I saw wasn't good. I didn't want to do that for the rest of the stdlib either.
If Araq has become the bottleneck, then maybe something needs to change in your process, because as you can clearly see I offered to be the one to make the changes.
And to be clear, I'm not angry about it, I just chose to spend my time elsewhere. Intentional or not, the language dev's as a whole could be doing a better job getting other people involved.
My understanding being that a systems programming language is one that can be used to bootstrap itself and build a full OS stack with the exception of some Assembly for interfacing with the underlying hardware.
As of Go 1.6, the language certainly fulfils this description.
If having a GC hinders certain use cases in systems programming, that is an orthogonal issue.
Edit: Looking forward to the day someone bothers to write an Oberon like OS in Go and HNers will keep on downvoting those that favour memory safe systems programming languages.
At this moment, the kernel used is a BSD kernel, but we are working now on making Go run on the bare hardware and on hypervisors. When that is done, the BSD kernels will be replaced with our own one.
So if that is enough to be a systems programming language, then C# is also one on account of the Singularity OS[2].
[1]: http://lsub.org/export/clivesys.pdf [2]: https://en.wikipedia.org/wiki/Singularity_(operating_system)
Both projects influenced the design of native .NET on Windows Phone 8, followed by .NET Native, IILC and CoreRT.
Also many of the C# extensions on Midori are being explored for C# 7 and future versions.
C# when compiled to native code is not much different than using Modula-3, in terms of available features. It just needs a little more fine tuning in terms of features.
I like performance in my systems programming languages and you won't get that if you can't control where your memory lives.
Many OSes like Oberon, Cedar, Topaz, SPIN among others were written in languages without direct support for it, so it is possible to write an OS without it.
The runtime package just needs to offer the services for it, just like many C functions are actually implemented in Assembly.
(Right now I think it is, but I know there's a Go issue dealing with a function like this, and one of the comments was about potentially creating `a` on the heap and returning an invisible pointer instead of the actual 1e9 bytes.)
func foo() (a [1e9]byte) { return a }Still, there are quite a few scenarios that could be served by such implementation, instead of being implemented in C or C++.
As I mentioned in another thread, any embedded board computer similar to a Raspberry PI could most likely be easily targeted by such cross-compiler.
People use the phrases without acknowledging or without realizing that there is a great breadth (and sometimes controversy) in what people take them to mean. I think you're doing it right defining it where you use it.
As always, a language and its implementations are not the same thing.
It is nothing special to have a bare runtime that would enable a Go application to be statically compiled and deployed into a Raspberry PI, for example.
It just needs someone to create such Go compiler.
Of course, such work would probably be a nice thesis project for OS design classes.
And sometimes some people (like me) would argue that C++ (11&14) is actually that language.
A subset of C++ is actually very perfomant and safe for most of the uses.
The memory safety track records of large-scale software written in modern C++ disagree with you. I don't think this is a tenable argument.
C++11 and C++14 don't really add any memory safety features over C++03 plus user-defined reference counted smart pointers. In fact, I think there's a reasonable argument to be made that C++11 is less safe in practice than this use of C++03. If you use rvalue references and move semantics a lot (since "use-after-move" is now a very real problem), or if you use lambdas a lot (since it's so easy to make dangling upvar references).
Yes we cannot get rid of the C underpinnings, but there are enough features to write safe C++. At least when using it alone or in small teams that value safety.
I have been using it in anger on my side projects for mobile s.
Why use it in spite of my rants about safety?
Tooling.
Using C++ across Android and Windows Phone is already an extra layer of pain (thanks NDK) with first class support from the platform owners.
Using Go, Swift, Rust would increase that pain level quite higher given the actual lack of comparable tooling.
Meaning build scripts, IDE, debugging tools, packaging and APIs.
C# doesn't suffer as much from it thanks to Xamarin, but I would only use it in commercial projects.
I really would wish that one of those languages would eventually be adopted across mobile OS SDKs as C and C++ currently are.
Already had a look at Rust for Windows Phone, but COM support is not yet there to the level expected by WinRT as C++/CX allows. Assuming I was looking at the right place.
No, there aren't. Empirically, modern C++ projects are not memory safe. Maybe when you qualify it with this:
> At least when using it alone or in small teams that value safety.
But I suspect the reason it looks that way is actually this: Projects that are developed alone or with small teams tend to be less important projects that people don't try to attack and/or find memory safety problems in. The lack of people actively looking for memory safety problems makes it look like C++ projects are safe, but in reality they aren't. If solo/small team projects in C++ had anywhere near as many people looking for holes as big ones like browser engines, then they'd fall over too.
I don't have any hard statistics for this, but given the consistent memory safety track record of C++ projects I think I'm more likely to be right here than wrong. I've written small projects in C++ and I don't think they're memory safe, even though I don't know of any areas where they aren't. I just know what the statistics are.
Given myself as example, in spite of all my rants about security that you well know, I still use C++ as my portable business code between mobile platforms.
However I always compile with all warnings as errors, fully embrace C++ STL types for safe programming and do regular static analysis, exactly to minimize as much as possible the errors that might still miss my attention.
If I would be focusing just in a single platform, then I would surely choose between Swift, Java and C# respectively.
Using Rust cross platform, across IOS and andriod, wasn't really that difficult to do...sure, the tooling isn't fully there yet. But I don't think it's any harder than c++, if anything, easier. Cargo is amazing and what makes Rust really nice to use. The community will create better IDE's and an ecosystem of tooling for cross platform, both mobile and desktop OS's. I believe Go also has some fairly popular mobile cross platform libraries as well that are probably much further along than Rusts'.
I can't say for Windows phone as I have never done anything for it and I think it's dead...
For the time being it surely is.
With C++ I just download XCode or NDK/Android Studio/Visual Studio.
I don't need to bother with FFI on iOS and Windows.
On Android I just need to bother with JNI FFI, not additionally how to call JNI from the respective language.
The IDEs show visual representation of data structures when debugging and two way editing between C++ and the respective platform language.
With Rust or Go AFAIK I need to use Mac OS X or GNU/Linux for cross-compilation. Need to compile that toolchain myself and write my own wrappers to OS APIs.
Also not enjoying the same IDE experience isn't that much fun.
> I can't say for Windows phone as I have never done anything for it and I think it's dead...
Except the APIs are the same as Windows and XBox are moving to, even if WP10 is at the end of the road now with the last update misfortune.
So if Rust or Go want a place in Windows Store for desktop and XBox apps, they need to get along with WinRT APIs.
Yes, those languages will surely improve but if I have 1 hour to work on a side project, I rather spend that hour coding and not fixing toolchain issues.
If I had more time, I would surely like to help to improve the situation, but sadly time is limited.
It wasn't too bad, though an inability to read C header files is kinda a pain. And for any target you are going to want to wrap things up in a "Rusty" api, as dealing with cstrings is kinda a pain.
Still, it'd be a pain to go from C strings to C++ strings, and hook new/delete up to kmalloc/kfree.
Nah: https://users.rust-lang.org/t/multirust-0-8-with-cross-std-i...
Before this feature the cross libs existed, just that there was no UI for downloading and installing them.
I always thought Go is less suitable as C++ replacement due to its GC. Another candidate which could be a dark horse is Swift.
Some newer languages that try to fill the niche are D, Nim, Rust and Go. Can't say much about Nim, it seems to be in the background. Go started as a systems programming language, but since then they backed out on it and it's mostly pushed as nicer C and webservice language. That leaves D and Rust on the field. Both are competing for the same audience, but Rust has the advantage of a popular brand backing (Mozilla) and a potential killer app (Servo, Mozilla's new work in progress browser engine). It also seems to have a clearer vision because of Mozilla backing and constant dogfooding in Servo. Meanwhile D is more of a design by community, as a result it gets not always the features that are needed or people want, but the features people are willing to work on. Also, D gets a lot of backlash from the game dev and system programming languages communities for using a (not too great) garbage collector, whereas Rust uses memory management somewhat similar to C++ model with RAII/smart pointers, however greatly improved and with much stronger static checks.
I think people just view Rust as the language that could actually be the alternative to C++, because there is a place for a language that is safer to use than C++, yet performs at similar speeds and doesn't handhold you like C#/Java do.