Nimrod for C programmers
github.com
github.com
Other than that, Nimrod has been a pleasure to use. If you need a metaprogramming language to generate C, use Nimrod.
It's quite common to end up needing to use both signed and unsigned types in the same expression and to end up invoking undefined behaviour trying to protect yourself. C overflow semantics (i.e. undefined) in particular.
Signed types should be the default, and overflow should be defined as wrapping two's complement. There's very good reasons why C# and Java chose these defaults.
The places where unsigned numbers are justified are incredibly rare: bitwise operations, and working with >2GB heap sizes on 32-bit machines. That's about it. In almost every other circumstance, using a signed type (the next size up if the range is inadequate) is better.
Especially pernicious is using something like size_t for things like container entry counts. It's very common to add and subtract with container counts, and very easy to end up inadvertently wrapping or mixing with signed types in common container algorithms.
PS: my experience was deeply coloured while working on the Delphi RTL, making it overflow safe. It was not trivial.
PPS: it was revealing that the comments on the article arguing in favour of unsigned types repeatedly made programming errors and invoked undefined behaviour. That's because unsigned types are misunderstood and misused.
Fourth case - situations in which you have a scalar quantity, and do not care about positive or negative.
Overflow is a separate issue and you must either make sure you either know the range of the data and pick an appropriately sized storage type, or explicitly check for overflows.
I just started working on this kind of application (well, USB protocols, but same issues) in Nimrod.
First of all: just do "import unsigned" and you can work with unsigned just fine. I don't see why this is an issue, any more than that you have to do "import libusb".
Second: I found that, knowing that unsigned types are a second-class citizen lead me towards writing a much better library. It encourages you to expose only signed integers to the next level up in the app. The next thing I realized is that this will very quickly cause Nimrod programmers to write and adopt a "Protocol Buffers"-like library for dealing with network protocols, binary files and similar things. And that will be a much better solution than dealing with things like unsigned integers and endianess in manual code.
I don't really see how unsigned are that much of a problem that they have to be dealt with specially...
This is actually where you have to be really careful, because there can be corner cases surrounding zero. E.g.:
for (i=len-1; i>=0; i--) { ... }
Yes, either ints or unsigned numbers can overflow, but underflowing a natural number is much more common.Don't take my word for it, ask Bjarne Stroustrup or Chandler Carruth: http://stackoverflow.com/questions/18795453/why-prefer-signe...
I read that page, or at least some of it, it mostly seemed to be opinion rather than solid reason.
No. The code is correct in that regard if you use int for i and len. Having to check len is an artifact of using unsigned.
> I read that page, or at least some of it, it mostly seemed to be opinion rather than solid reason.
I'm not talking about the comments. I'm talking about what Bjarne Stroustrup (the designer of C++) and Chandler Carruth (Google's LLVM lead) are saying in the video (and which is summarized in the top comment).
I did not watch the video, no time right now. The top comment tells us what they think, not why.
Even iteration is not always over containers or some other structure in a way where the boundaries can be inferred and do not have to be computed, and not always in an order that is implemented by an iterator (for example, some in-place sorting algorithms).
for i := 0 to len-1 do begin ... end
This is counting up, not counting down, and len-1 is still going to be either 2^k-1 or result in an error when you're using unsigned integers.While loops also don't fix it (not to mention that they have their own problem, such as forgetting or misplacing the iteration step). The primary issue is that len-1 is underflowing. Yes, you can avoid calculating len-1, but by that token, no error is ever a problem, because they can all be avoided with enough diligence and foresight. In that universe, Heartbleed never happened and the Ariane 5 never blew up. It is, however, not the world we live in.
This is also a non-issue in C++ as you have iterators and counting loops are easy to abstract away using counting iterators or <algorithm> style functions.
I take it you do not work with bare metal at all.
Most general purpose programming languages don't have features specific for web-programming imported by default.
Nimrod supports bare metal programming just as fine as C. You just have to import the relevant modules, just as you'd import the URL module if you were writing a web-server.
Nimrod aims at being approachable to some of the Python/Ruby crowd as well, who are used to numbers that are always signed and can't overflow. Defaulting to the biggest available signed integers, and discouraging library writers from throwing unsigned integers at you for no reason is a decent compromise.
What do you mean by that? Don't you think that having to do "import unsigned" before they use unsigned types, make some people think twice? Don't you think a few people might think "Do I really understand this feature? Should I perhaps read the documentation for this module?"
More importantly though, not having unsigned types, as in Java, means that we have to use 16-bit int to store a byte, 32-bit int to store a 16-bit unsiged etc.
Btw, if you check any C code, you'll notice that is vast majority of cases negative values of int are actually invalid from the business logic point of view.
..., not having unsigned types, as in Java, ...
Unsinged integer exist in Nimrod, you just have to import unsigned
when you use them.Looking at Nimrod's AST macros (which I haven't used) I imagine Julia as a closer competitor to it than Go. They both appear to come from a "programmable programming language" mindset, though Julia arguably more so because it's homoiconic.
- No curly braces around anything.
- Whitespace seems to be important, just like python.
- Some nonsense about unsigned integers.
The best thing I saw was that it has C's explicit-size integers. The one thing I wish every C program would use rather than having configure guess which basic type it should use.
- No curly braces around anything.
- Whitespace seems to be important, just like python.
That might be a 'bug' for you, but that's a feature for most. At least for Python, Yaml, Coffeescript, Jade, Stylus and Nimrod users :)Not interested anymore.
The C brackets stuff is a mess:
http://www.slate.com/articles/technology/bitwise/2014/02/app... (The error above is not only about the gotos)
Is inconsistent. Cause bugs. Can be write like brackets don't exist (http://www.andromeda.com/people/ddyer/topten.html), nullify your argument. Pick the above link and count how much problems are masked with the illusion of the brackets.
Is unnecessary (add noise just to help the parser), and python, haskell and other languages show that could be done better.
Yes, it's a pain to work with massively nested logic, but it's actually python's way of saying, "dude! keep your functions small and simple!", which I've come to appreciate.
It's one of the things I like about python, actually, is that it really tries hard to encourage you to write good code. Once things start looking ugly and hard to understand, it usually means that the logic & method you're trying to implement isn't a great choice.
The C99 stdint types really should be second nature to C developers, it always surprises me how many just haven't heard of them.
Do you find curly braces to have some aesthetic? That you like to see it in code for its own sake?
After looking through the original patch and the current version, I found that the bug was caused by significant indentation. The maintainer had decided to add comments and accidentally indented the code one more level, making it part of an if branch. Braces would have prevented this.
That's not to say that Rust isn't convenient, but the quest for strong memory safety & performance guarantees doesn't come for free: satisfying the borrow checker can get a little tricky at times.
An example of this: Rust's type system is designed to allow things like building a safe shared memory abstraction[1], that is, a shared memory type that enforces non-thread-safe data can't be shared without being inside a mutex. My reading of the Nimrod FAQ[2] ("An unsafe shared memory heap is also provided") implies this isn't (as easily) possible in Nimrod.
(Disclaimer: I know a lot of Rust, but very little Nimrod.)
[1]: http://static.rust-lang.org/doc/master/sync/struct.Arc.html
Nimrod's GC is not thread safe, so you have to use raw unsafe allocation and deallocation in order to share objects between threads (last I looked anyway). This is in contrast to Rust, for which memory safety is important even in multithreaded scenarios.
My understanding (from talking to Nimrod's author) is that the concurrency model is still being finalized, but that safe shared memory is going to be part of it.
All in all, I have looked into Nimrod a few times. It is very, very cool. One guy is even using the Obj-C bridge to run a core of an iOS app on it. You could theoretically now write a web API, an Android app core (not the GUI), and an iOS app core (not the GUI), and have them running all off the same logic written in Nimrod.
I highly recommend checking out the forum and see the caliber of stuff these guys are putting out. Very impressive stuff.
I expect that one could get the same performance in C, though unrolling the top levels of the recursive search would be a bit more cumbersome and having efficient bound checks for arrays in Nimrod helped in debugging some variants of the algorithm.
In short, the benefits of Nimrod here were expressiveness and safety rather than raw performance (though having performance of optimized code that is competitive with C hardly hurts, either).
That made my day
That being said, I believe a big draw is its metaprogramming abilities. In Nimrod, metaprogramming is done using Nimrod. In C++ you have the very limited, backwards compatible to C, C++ preprocessor and template metaprogramming. C++ is progressing slowly towards metaprogramming in C++ with constexpr. constexpr support is much stronger in C++14.
D is similar. In D, the metaprogramming language is (I believe) D itself, or at least very similar to it. D also has strong support for compile time function execution.
Nimrod also does a lot more to guarantee safety. C++ can only do so much, due to its (mostly) backwards compatibility to C.
Like, imagine you could write Python instead of C++, except that it only ran 10% slower instead of 10,000% slower.
C for nimrod programmers