I am a new user of Nim (< 1 week) and I must say I like the language very much and I am pleasantly surprised by what has been done so far, more so considering the small community. Kudos to everyone involved!
That said, I come from a managed, mostly functional, background, but I am about to start a project that will require speed and a precise control over the memory. Having discarded C - since I will need a certain level of polymorphism and I appreciate a decent type system - I am evaluating a few languages that could be suitable for the task, such as C++, Nim, Rust, D, ATS or Parasail.
So far, I have found Nim to be a clear winner in combining expressive power, static checks and simplicity. My only worry is about the orthogonality of language features - that is, I prefer having few features that can be combined in powerful ways rather than having available every feature under the sun.
Nim looks a quite complex language both from the syntactic and the semantic point of view. For instance, a previous question of mine on this forum was solved by turning
type Generator*[T] = generic x
next(var x) is T
into
type Generator*[T] = generic var x
next(x) is T
It is still not clear to me how both are syntactically valid, and exactly what is the difference in meaning among the two.
Let me give other examples (I hope to get this stuff right):
All of this, while making Nim very powerful, also increases a lot the complexity on the programmer. I have the feeling (but not the proof!) that the same level of power can be achieved with a more limited set of basic features. It would be a shame if Nim turned out the level of complexity of C++ (not saying that it currently is!).
Hoping this does not come as a rant, the real question is: are there any plans to simplify the grammar and the feature set of Nim after 1.0 is out?
A good thing about Nim - you don't need to know it entirely (or even a half) to program comfortably in it.
> there are both static polymorphism with generics and typeclasses, and dynamic polymorphism with objects and methods Just use methods when object's exact type can't be known at compile-time.
> the module system allows both include and imports Basically you need to use import, include just inserts contents of a file in-place, it's needed sometimes (e.g. you need to split a large module into parts, but include it as one module), but you may as well just forget of its existence
type inference allows to avoid naming a return type, and automatic generic allows to avoid giving types to function inputs, but the two cannot be combined. This seems like a weird interaction between disjoint features.
I don't know what you mean by this. Please give an example.
there are various forms of metaprogramming, ranging from templates (immediate or not) to macros, and also source code filters to generate code
The template and macro system might be simplified but it's unclear to me how this can be accomplished. IMO we should specify the type system via ASTs as well and unify generics and macros but this is a problem that I have been thinking about for ~8 years now and it's still not clear to me whether that is possible at all!
Source code filters can easily be replaced by macros and templates but I don't consider them complex, the implementation is 300 lines of code in the compiler. You cannot count the number of features easily and then say "too many features, too complex!", you have to look at what complexity they actually produce. Some things are very orthogonal (source code filters, memory regions for pointers) and others interact with every other feature (generics, templates, the type system).
various pragmas are used to denote a lot of different stuff, from an effect system, to annotating iterators that close over the environment, to importing C files
I hear this criticism quite a bit, but I don't consider it fair. Most pragmas are related to the extensive FFI. Then it's also used for calling conventions which are also FFI related. Then it's used as a general annotation mechanism, but Java, C#, C++, Python all have grown that too and in these languages it's never been considered a problem. The effect system is very much underrated, IMHO.
the module system allows both include and imports
include is just file inclusion, it doesn't affect namespacing or separate compilation so again, you list 1 feature that is essential and adds significant complexity (import) and then 1 which doesn't (include).
All of this, while making Nim very powerful, also increases a lot the complexity on the programmer. I have the feeling (but not the proof!) that the same level of power can be achieved with a more limited set of basic features.
Well if you come up with concrete simplifications I'm all ears, but usually when I strive for simplicity I only get the blame: forward declarations, no classes or interfaces in the core language, no nil-safety, no real algebraic datatypes, no Rust-like lifetime annotations, etc.
Personally I would throw away multi-methods completely but now some projects use them extensively and they provide a smoother transition for people coming from classical OO based languages.
@LeuGim
A good thing about Nim - you don't need to know it entirely (or even a half) to program comfortably in it.
I know: I have been productive in Nim since day one! I cannot say the same of Rust, for instance. Not to bash the language, which has very interesting concepts, but the learning curve is definitely steeper in Rust.
For instance, a direct port of the Python version of a small benchmark I had done has immediately resulted in impressive performance, just below Rust. But the Rust version has required maybe ten times as much and a lot of help online
@Araq
I don't know what you mean by this. Please give an example.
Sorry, I was under the impression that implicit generics and auto return type would not work together, so that the following would not compile:
proc sum(a, b): auto = a + b
echo sum(5, 3)
It turns out it does.
I hear this criticism quite a bit, but I don't consider it fair. Most pragmas are related to the extensive FFI. Then it's also used for calling conventions which are also FFI related. Then it's used as a general annotation mechanism, but Java, C#, C++, Python all have grown that too and in these languages it's never been considered a problem. The effect system is very much underrated, IMHO.
In fact, I like a lot the presence of an effect system! It is just that putting a lot of things into pragmas does not make them less part of the language. It is true that Java uses annotation quite extensively, but I see this as a shortcoming of the language. In Python, annotations (in the form of decorators) are just syntactic sugar over function call. But then again, Python is dynamic, which simplifies a lot of stuff.
include is just file inclusion, it doesn't affect namespacing or separate compilation so again, you list 1 feature that is essential and adds significant complexity (import) and then 1 which doesn't (include).
I know that they are different, it is just that most languages get away with just imports. My impression is that small details like this add up at the end.
Well if you come up with concrete simplifications I'm all ears, but usually when I strive for simplicity I only get the blame: forward declarations, no classes or interfaces in the core language, no nil-safety, no real algebraic datatypes, no Rust-like lifetime annotations, etc.
Then I am even happier to have presented you a vote for the opposite camp! :-)
For instance, I have followed the discussion about interfaces. It seems to me that typeclasses could very well replace them, save for the fact that the former are opt-in, while the latter are implemented implicitly. Now, I am rather agnostic about this particular fact: for me Nim typeclasses are enough, but I wouldn't mind having to type instance X Y where ... as in Haskell. What would be sad would be having both mechanisms to define a set of types.
That said, I know it easier to ask for usable, unifying abstractions than to find them! Even if I still have a feeling of complexity, I find Nim both easy and powerful. I hope I will be able to spread the word among my colleagues! :-)
@Araq Personally I would throw away multi-methods completely but now some projects use them extensively and they provide a smoother transition for people coming from classical OO based languages.
I'm curious, would you simply omit them or would you provide another language feature for dynamic dispatch? If the former, I'm curious as to how you'd handle those problems that seem to map well to some dynamic dispatch approach (e.g., the Servo/Rust guys cite modeling the DOM as being unpleasant without some OO); if the latter, of course I'd be interested in what you'd replace them with.
I haven't used Nim's multimethods "in anger" (that is, for real work) yet, but from playing around they seem to work well and fit with the rest of the language. Well, I suppose that since they're a dynamic/runtime feature that they don't fit with Nim's static metaprogramming thrust, but they're orthogonal to it.
For instance, a direct port of the Python version of a small benchmark I had done has immediately resulted in impressive performance, just below Rust. But the Rust version has required maybe ten times as much and a lot of help online
Should be easy to get faster, see https://github.com/andreaferretti/kmeans/pull/2
That said, I know it easier to ask for usable, unifying abstractions than to find them!
And that is not even as desirable as you perhaps think it is. I appreciate the rule of least power and so Nim is a staged language: We have templates even though macros can do everything that templates can do and much more. We have let even though var can do more and we have for, while and if even though goto could do the same.
@Nikki
You do realize that you are making Nim a disservice, right? I appreciate Nim, and in fact I happen to like it more than Rust. I would like to showcase Nim to my colleagues to get them involved, but I am pretty sure that comments like yours will help get Nim a bad first impression. If Nim stands on its own, there is just no need to bash other languages.
Moreover, the people that are currently toying with Rust are the ones who could be most interested in Nim, as the languages serve the same niche. Derogatory comments towards Rust can only turn those people away.
jpoirier, can you please try to keep this discussion focused on the technical matters we are talking about? We're trying to discuss the pros and cons of different programming language features here, but you keep disrupting us with your petty insults.
Do you have anything useful to contribute to the discussion we are having about how Nim avoids many of the pitfalls of languages like Rust, JavaScript, Ruby and PHP?