I'm coming from Python and I would like to know about Nim and its extensibility because I have been searching for an extensible language. I was thinking on Common Lisp but I came across Nim and I'm really interested on it.
Has Nim all the metaprogramming features that Common Lisp has? I mean, can I implement any programming paradigm I want with Nim's metaprogramming? In the main site is said that Nim can be extended to various programming paradigms, but not says for any paradigm.
Has Nim an extensible syntax? I searched a lot about this and I couldn't find anything about this.
And about Metaclasses, can it be implemented in Nim using its metaprogramming facilities?
Has Nim all the metaprogramming features that Common Lisp has?
No as there are no reader macros in Nim but the better (IMO anyway) alternative is to have a compile-time parser that operators on string literals.
Has Nim an extensible syntax? I searched a lot about this and I couldn't find anything about this.
No but the hard-coded syntax is so flexible that it doesn't matter.
And about Metaclasses, can it be implemented in Nim using its metaprogramming facilities?
Depends on what you want to accomplish.
Nim is really extensible. Features like async/await, -> style anonymous functions, and string interpolation are defined in the stdlib, with little to no language level support. I have a lot of Ruby experience, a language know for its metaprogramming capabilities, and Nim is significantly more extensible.
Nim metaprogramming happens at compile time, which is very different from Ruby or Python. You can generate types based on the contents of a database, or the results of an http request, but once the program is built it isn't changing until you rebuild it. You can't introduce new types or functions at runtime (technically you could with the VM, but most people don't use Nim that way).
Can it support any programming paradigm? Who knows. It really depends on what level of support you're looking for, and how much work you're willing to do. You could write a Nim macro that imports and compiles code written in a completely different language if you wanted to, but at that point you're basically writing a new compiler inside of a macro, which is going to be a lot more trouble than it's worth most of the time.
If you give more details on what you're hoping to do you'll probably get more useful answers. Whatever it is, I'd say it's pretty likely that you could do it with Nim, but it could be a big undertaking. Or it could be a 5 line template that takes 2 minutes to write.
@Araq
No as there are no reader macros in Nim but the better (IMO anyway) alternative is to have a compile-time parser that operators on string literals.
Sorry for my ignorance, I'm not a experienced Python programming, what I do is just some tools for doing somethings in my system; can these reader macros be implemented in Nim using its metaprogramming? I mean, define a macro from another macro that will do the job at compile-time
No but the hard-coded syntax is so flexible that it doesn't matter.
As @dsrw said, it's possible to write a sub-language in Nim, this is very interesting.
Depends on what you want to accomplish.
I found some examples of implementations of Metaclasses in Racket and Scheme:
https://github.com/brownplt/lambda-py/blob/master/base/python-desugar.rkt#L653-L704
https://github.com/peteflorence/Scheme/blob/master/p4-scheme-files/oo-eval.scm~#L531-L571
If Nim macro system is derived from LISP, it will be possible to implement metaclass support in Nim. Very good new from me.
@dsrw
Can it support any programming paradigm? Who knows. It really depends on what level of support you're looking for, and how much work you're willing to do. You could write a Nim macro that imports and compiles code written in a completely different language if you wanted to, but at that point you're basically writing a new compiler inside of a macro which is going to be a lot more trouble than it's worth most of the time.
Very interesting, I could write a Forth implementation in Nim and have all the speedups of GCC, so having a real-time Forth.
If you give more details on what you're hoping to do you'll probably get more useful answers. Whatever it is, I'd say it's pretty likely that you could do it with Nim, but it could be a big undertaking. Or it could be a 5 line template that takes 2 minutes to write.
I'm seeking for an universal language that will will stay with me for the rest of my life. Just like: https://wiki.c2.com/?UniversalProgrammingLanguage
It's just like finding/choosing the right girlfriend, it takes a lot of time. =)
I take a look in Common Lisp/SBCL and honestly I hated (but I respect the LISPers around the world): runs in a app image, few portability to other platforms (SBCL in Debian is avaliable just for 6 different ones), less performance compared to C, garbage collection in fact not possible to disable without having to use FFI and write a bunch of things to manage the memory (so, no speedup).
I found Nim amazing for its performance when doing manual memory management: https://github.com/frol/completely-unscientific-benchmarks/blob/master/README.md#linux-arch-linux-x64-intel-core-i7-4710hq-cpu-1
As @dsrw said, it's possible to write a sub-language in Nim, this is very interesting.
You could have a macro that took a filename as a parameter, read the file, ran it through a parser of some sort, then used the parse tree to generate Nim code which would be compiled into your program. It would be a lot more work than just making a DSL with Nim syntax, but it's an option if you need it. As Araq says though, Nim syntax is really flexible. The Nim code you pass to a macro doesn't need to mean anything to the compiler, it just needs to be parsable. That means that it's still whitespace sensitive, blocks still have to start with :, your braces have to match up and things of that nature, but apart from the basic structural elements the code can be whatever you like.
Writing your DSL in Nim syntax and reading it with a macro has a lot of advantages. You don't have to write a parser. The elements of your parse tree all have line/file info associated with them, making it fairly easy to surface useful errors. You get lots of tools for easily transversing and searching your tree, and enforcing certain conditions about the code that's passed in. And you only have to fiddle with the bits that you actually care about. If you want to handle some constructs as normal Nim code you can just pass them along untouched, and the Nim compiler will do all the work.
Nim is definitely the closest thing to a universal language that I've found. There are things that it isn't particularly good at, but that's mostly due to a lack of libraries/ecosystem rather than a fundamental limitation of the language. One of my primary uses of Nim is as a super simple, logo inspired language to teach kids to code. The code doesn't look much like normal Nim. It has a completely different way to define functions, eschews immutability, puts lots of things into the global scope by default, and has custom syntax for drawing things and making simple behaviours. It runs in an interpreter (the NimVM, what Nim normally uses to evaluate macros and config files), makes everything reloadable on the fly, and doesn't even follow the normal Nim style conventions. It's probably the furthest thing from what Araq intended when he started writing Nim, but yet Nim is a great fit. I'm almost positive that I would have abandoned my project ages ago if I had started in any other language.
The Perl community has an adage along the lines of "easy things should be easy, and hard things should be possible". Nim does that really, really well.
@dsrw
You could have a macro that took a filename as a parameter, read the file, ran it through a parser of some sort, then used the parse tree to generate Nim code which would be compiled into your program.
Awesome, it means that Nim is a language with almost infinite possibilities.
It would be a lot more work than just making a DSL with Nim syntax, but it's an option if you need it. As Araq says though, Nim syntax is really flexible. The Nim code you pass to a macro doesn't need to mean anything to the compiler, it just needs to be parsable. That means that it's still whitespace sensitive, blocks still have to start with :, your braces have to match up and things of that nature, but apart from the basic structural elements the code can be whatever you like.
Writing your DSL in Nim syntax and reading it with a macro has a lot of advantages. You don't have to write a parser. The elements of your parse tree all have line/file info associated with them, making it fairly easy to surface useful errors. You get lots of tools for easily transversing and searching your tree, and enforcing certain conditions about the code that's passed in. And you only have to fiddle with the bits that you actually care about. If you want to handle some constructs as normal Nim code you can just pass them along untouched, and the Nim compiler will do all the work.
It was a question about Nim's extensibility because I'm young and I'm choosing a definitive language for all my projects, I'm not the kind of person that creates projects in multiple languages, maybe someday I create my own toy language as a macro in Nim and use for my possible needs.
Having Nim the possibility of creating sub-languages is something awesome to me.
And about Nim'm syntax, is just perfect for me that have been studying Python for my personal needs.
Nim is definitely the closest thing to a universal language that I've found. There are things that it isn't particularly good at, but that's mostly due to a lack of libraries/ecosystem rather than a fundamental limitation of the language. One of my primary uses of Nim is as a super simple, logo inspired language to teach kids to code. The code doesn't look much like normal Nim. It has a completely different way to define functions, eschews immutability, makes many things global by default, and has custom syntax for drawing things and making simple behaviours. It runs in an interpreter (the NimVM, what Nim normally uses to evaluate macros and config files), makes everything reloadable on the fly, and doesn't even follow the normal Nim style conventions. It's probably the furthest thing from what Araq intended when he started writing Nim, but yet Nim is a great fit. I'm almost positive that I would have abandoned my project ages ago if I had started in any other language.
Now that I just know that Nim is so expansible I can say the same: An universal language.
It's a pitty that some big company/big tech like Mozilla doesn't support Nim team, Rust crates have 95000+ packages and Nimble 2000+, I hope someday the Nim count grows very much.
I can say that I'm falling in love with Nim after reading all the answer in this topic, I realized that Nim is that universal language I always dream to.
I hope that in 2~3 years I can make my projects, modules, macros and extensions and help Nim grow. Somethings I'm going to prioritize is Linux namespaces handling and low-level network stuff like manipulating TUN/TAP devices. Maybe in the future I can have an idea of a large project to write in Nim.
Thank all of you for the answers, @Araq and @dsrw; I'm sure in 50 years I will still remember this topic and this conversation.
รต/
It's a pitty that some big company/big tech like Mozilla doesn't support Nim team,
I think it'll be the long slow path for Nim, unless it gets a "killer app" like Ruby on Rails. Python took many years to peculate before the data science stuff really kicked it off.
Rust crates have 95000+ packages and Nimble 2000+, I hope someday the Nim count grows very much.
I had to learn Rust for a work project recently and while there are 90,000+ crates out there so many of them were just to work around limitations in Rust itself or it's really limited stdlib. It was frustrating spending hours evaluating crates, many with almost no documentation, to try and solve some simple task. :/
Doing pretty much anything in Rust requires adding a crate or two. I'm not really exaggerating either -- there are probably half a dozen or more crates for initializing arrays. The traits system also encourages lots of crates just to provide the trait scaffolding.
Rust definitely still has the advantage in ecosystem, but I don't think it's nearly as large as the 2k vs 95k makes it seem.
For loop on arbitrary numbers of inputs https://github.com/numforge/laser/blob/e23b5d6/benchmarks/loop_iteration/iter05_fusedpertensor.nim#L156-L159
See also https://github.com/mratsim/compute-graph-optim for progressive experimental steps to jump from for loop on arbitrary number of inputs to a matrix expression compiler running in macros.
You could have a macro that took a filename as a parameter, read the file, ran it through a parser of some sort, then used the parse tree to generate Nim code which would be compiled into your program. It would be a lot more work than just making a DSL with Nim syntax, but it's an option if you need it. As Araq says though, Nim syntax is really flexible. The Nim code you pass to a macro doesn't need to mean anything to the compiler, it just needs to be parsable. That means that it's still whitespace sensitive, blocks still have to start with :, your braces have to match up and things of that nature, but apart from the basic structural elements the code can be whatever you like.
I wouldn't want to bump this topic again, but I have a question:
Can the code read be on the same .nim file instead of a separated file?