Although I haven’t coded in it, I found this language is interesting in a lot of ways. Drawing on the traits of languages in the title already gives it a ton of potential if done right. It does have a REPL already. It’s key components started at under a thousand lines of code each. It was written in Nim, leverages it for GC + concurrency, and can use either Nim or C for performance reasons. I was strongly considering writing a LISP or Smalltalk variant in Nim for past few months for similar reasons. The output would be very different since I want to leverage various analyses for safety-critical tooling. The common thinking seems to have been Nim as a strong, base language for macros, portability, and performance. As in Spry, one also needs something to drop down to when new language isn’t cutting it for whatever reason.
It’s going on the Bootstrapping page since it might be used for or inspire something along those lines. :)
Well, I just looked at its features and example code. What I saw was a language that had improvements in readability, power, and safety over C-style systems languages that also outputs C. That’s definitely nice. It has potential both on its own and as a point in design space of what to do next.
My personal favorites were always PreScheme and Modula-3 for right balance of power, compile speed, runtime speed, relative ease of machine analysis, and implementation simplicity. You will rarely if ever see that combo since the simple implememtations almost always trade off power, performance, or comparable safety. The complex ones hard to compile or analyze.
Before Rust, Julia, and Nim, my recommendation was to embed a version of Modula-3 in PreScheme with its macros and malleability to boost power. However, the syntax, default includes, and output would all be C to just drop it in the ecosystem. Add in Ada or Cyclone style safe-by-default properties. Although I never tried to build it, Ive gotten to see pieces of it form in other languages: Rust learned from Cyclone; Julia was a LISP internally that made including C effortless; Nim had Pythonic style abd macros compiling to C.
Still nothing has all traits of PreScheme and Modula-3 but I keep looking for anything close that might be sub/super-setted into such a language. I had even considered embedding Rust or Nim into Racket with its macros and IDE but it may be too much mismatch. Still hope, though, that system programming can get leaps above C/C++ with some increment better than Rust, D, Ada, or Nim in balancing prior goals.
Hope all that makes sense. Also, such a language woukd make my Brute Force Assurance concept easier that combines all the verification tooling of many languages for one. I think I already wrote about it here but not sure.
This looks quite good, and I love the idea of trying to bring back some of the most innovative (but currently impractical) aspects of Smalltalk, namely image-based environment and live coding. Rebol is also a very interesting ancestor.
You say impractical. What’s your thoughts on Squeak and Pharo? I watch them at a distance. So, hard to tell how practical they are/arent along with where they could use improvements theyd need.
I remember a talk about Seaside (Pharo web framework) where the live image ended up being in the 100mb-1Gb range. If you listen to Alan Kay’s remembering the Alto, Smalltalk sure looks very practical. However, nowadays the Smalltalk environment lacks integration with modern GUI toolkits – it basically functions as an island. Objective-C and Cocoa inherit a lot of the good stuff from Smalltalk, like late binding and dynamic method dispatch, which together with Interface Builder allows for interesting hacks and dynamic reconfiguration of UIs.
I hear you. I saw this as a benefit and drawback when looking at Squeak and Oberon for reproducible environments for software done in CompSci papers. Their cores are so simple to implement that it makes the reproducible part easy. Just port some key libraries or API’s to them. Keep those updated. Done. Yet, them being like an island, esp the GUI’s, means that most developers are unlikely to use them. They’ll want the tools and styles they’re comfortable with. Plus, Worse is Better says to always invest time in that direction since it’s where the momentum usually goes. (sighs)
Good that the Apple stack keep some of its good features. Then, they have UNIX underneath for folks that want that. I can see why many stick with them over Linux.
The syntax looks very terse, but this language does indeed look very interesting. I like the emphasis on live coding and that FORTH is one if its ancestors.
Is Nim a good language for creating (eventually) self compiling languages out of? Is it a good target for code generation (by which I mean do you have to fight it a lot if the language you’re implementing is different)?
I switched over to C from x86_64 assembly but would like to know what are good alternatives these days (currently, they are both hard to debug).
The readme in src/ talks about a sprymin.nim file but I wasn’t able to find it anywhere.
Honestly, I don’t know. I’d have to learn the language. I’ll ping @dom96 in case he can tell you something about the debug story or what low-level stuff people do in it. It’s in a state of constant change where I’d probably have missed stuff. Main page did say it at least outputs a stack trace when it crashes.
I know it’s has several features for metaprogramming. That gives it lots of power. Compiling to C with low-latency GC gives it broad applications. Given Oberon was used for OS’s, you could probably build most things with it so long as performance wasn’t too bad. It probably has an unsafe keyword or something for high-performance, manual management of memory for when GC can’t be tolerated. You still get benefits of a HLL in that case. If resource-constrained, you might try embedding something like C in as a DSL. Ivory language does the latter in Haskell. Techniques like Design-by-Contract help a lot in debugging. You should aim to knock out as many errors as possible ahead of time with a safe, high-level language that localizes them where possible. Then, contract assertions can help you find the rest esp combined with property-based testing or fuzzing.
Far as self-compiling, Nim is already implemented in Nim. It leverages C for performance. One can always build subsets that compile unoptimized to x86 if they want to compile something in itself. This is something many obsess about but that isn’t that important. That’s because traits we judge compilers on are same as a program: compilation speed, runtime speed, flexibility, safety/security. The compilers that produce fastest code are black boxes in unsafe languages you’d have to invest massive time into understanding. Those you can bootstrap and understand easiest will produce sub-optimal code you’ll likely not use. The last point is one should always use best tool for the job. Certain languages, like Ocaml, have features that make them inherently better at writing compilers balancing code size, safety, and performance. For extreme example, Ometa can implement things like JavaScript in a few hundred lines of code. Its tradeoff is conciseness and flexibility you’re not getting in C but way, way slower. Tradeoffs abound.
If you need both, then the Wirth languages and some Schemes are probably best. One has whole books telling you how to compile it with source code. Use them for trust/understanding or extract to C to leverage black boxes. For Scheme, you can use the easiest features to implement in a modern Scheme (eg Racket or Chicken) where you can use their optimizing compiler or extend a mini-homebrew Scheme you understand up to their level.
Those are the basic principles. Compiling in self just isn’t that important for 99.9+% of people’s use cases. It’s more a tradition of testing the language on something hard that the developers are sure will both stick around and get bigger. It’s not necessary, though, like with all the HLL’s written in C++ or whatever.
Thanks for the detailed reply! If you get ahold of them, I’d be much more interested to know how they intend to spin up the live environment, especially for something like the Squeak debugger. Even a simple curses or command line version (or curses) would be interesting if it can be made without a lot of code.
Nim does look like it has a number of nice properties and at least claims to aim for speed and allows manual GC. On the other hand, it does not seem to be very focused on its debugging tools. (Someone tried to remedy this)[https://forum.nim-lang.org/t/3076] and it does have source and line number info but presumably I can’t easily evaluate nim expressions and make calls in gdb (or Nim’s debugger if it had one).
Techniques like Design-by-Contract help a lot in debugging.
Assertions are used a bit; maybe I should do more. But I’d really rather have a better debugger/repl/live environment (preferably written in the new language itself). Part of the reason is that the spec changes a lot as I explore and a lot is even scrapped. In this case, the language and (core) library also changes so updating assertions and test is rather costly. The same reason applies to other techniques that needs a lot of time before I can run the program and visualize the result to decide if the program should be scrapped or not (having complete correctness is usually not needed to visualize).
Is there any way to gain certainty more quickly?
Nim is already implemented in Nim
I briefly looked at Nim’s compiler in Nim but it says that it was almost directly translated from Pascal and it is also quite large.
One can always build subsets that compile unoptimized to x86 if they want to compile something in itself.
I didn’t understand what you mean here. Subset of Nim, the new language or the Nim compiler?
Ometa can implement things like JavaScript in a few hundred lines of code.
I think the original Ometa is also hard to debug.
Its tradeoff is conciseness and flexibility you’re not getting in C but way, way slower.
Is it really that much slower? I thought you could use it for optimization using Ometa AST transformations.
Scheme
In Scheme flavours, is it easy to implement specific behaviours with respect to memory layout and computation? Do you only get a language equivalent to what you specified or can you get the same amount of control as assembly and C (and I guess without fighting the language too much)?
Although I haven’t coded in it, I found this language is interesting in a lot of ways. Drawing on the traits of languages in the title already gives it a ton of potential if done right. It does have a REPL already. It’s key components started at under a thousand lines of code each. It was written in Nim, leverages it for GC + concurrency, and can use either Nim or C for performance reasons. I was strongly considering writing a LISP or Smalltalk variant in Nim for past few months for similar reasons. The output would be very different since I want to leverage various analyses for safety-critical tooling. The common thinking seems to have been Nim as a strong, base language for macros, portability, and performance. As in Spry, one also needs something to drop down to when new language isn’t cutting it for whatever reason.
It’s going on the Bootstrapping page since it might be used for or inspire something along those lines. :)
Nick, you like Nim? I didn’t know that!
Well, I just looked at its features and example code. What I saw was a language that had improvements in readability, power, and safety over C-style systems languages that also outputs C. That’s definitely nice. It has potential both on its own and as a point in design space of what to do next.
My personal favorites were always PreScheme and Modula-3 for right balance of power, compile speed, runtime speed, relative ease of machine analysis, and implementation simplicity. You will rarely if ever see that combo since the simple implememtations almost always trade off power, performance, or comparable safety. The complex ones hard to compile or analyze.
Before Rust, Julia, and Nim, my recommendation was to embed a version of Modula-3 in PreScheme with its macros and malleability to boost power. However, the syntax, default includes, and output would all be C to just drop it in the ecosystem. Add in Ada or Cyclone style safe-by-default properties. Although I never tried to build it, Ive gotten to see pieces of it form in other languages: Rust learned from Cyclone; Julia was a LISP internally that made including C effortless; Nim had Pythonic style abd macros compiling to C.
Still nothing has all traits of PreScheme and Modula-3 but I keep looking for anything close that might be sub/super-setted into such a language. I had even considered embedding Rust or Nim into Racket with its macros and IDE but it may be too much mismatch. Still hope, though, that system programming can get leaps above C/C++ with some increment better than Rust, D, Ada, or Nim in balancing prior goals.
Hope all that makes sense. Also, such a language woukd make my Brute Force Assurance concept easier that combines all the verification tooling of many languages for one. I think I already wrote about it here but not sure.
This looks quite good, and I love the idea of trying to bring back some of the most innovative (but currently impractical) aspects of Smalltalk, namely image-based environment and live coding. Rebol is also a very interesting ancestor.
You say impractical. What’s your thoughts on Squeak and Pharo? I watch them at a distance. So, hard to tell how practical they are/arent along with where they could use improvements theyd need.
I remember a talk about Seaside (Pharo web framework) where the live image ended up being in the 100mb-1Gb range. If you listen to Alan Kay’s remembering the Alto, Smalltalk sure looks very practical. However, nowadays the Smalltalk environment lacks integration with modern GUI toolkits – it basically functions as an island. Objective-C and Cocoa inherit a lot of the good stuff from Smalltalk, like late binding and dynamic method dispatch, which together with Interface Builder allows for interesting hacks and dynamic reconfiguration of UIs.
I hear you. I saw this as a benefit and drawback when looking at Squeak and Oberon for reproducible environments for software done in CompSci papers. Their cores are so simple to implement that it makes the reproducible part easy. Just port some key libraries or API’s to them. Keep those updated. Done. Yet, them being like an island, esp the GUI’s, means that most developers are unlikely to use them. They’ll want the tools and styles they’re comfortable with. Plus, Worse is Better says to always invest time in that direction since it’s where the momentum usually goes. (sighs)
Good that the Apple stack keep some of its good features. Then, they have UNIX underneath for folks that want that. I can see why many stick with them over Linux.
The syntax looks very terse, but this language does indeed look very interesting. I like the emphasis on live coding and that FORTH is one if its ancestors.
Is Nim a good language for creating (eventually) self compiling languages out of? Is it a good target for code generation (by which I mean do you have to fight it a lot if the language you’re implementing is different)?
I switched over to C from x86_64 assembly but would like to know what are good alternatives these days (currently, they are both hard to debug).
The readme in src/ talks about a sprymin.nim file but I wasn’t able to find it anywhere.
Honestly, I don’t know. I’d have to learn the language. I’ll ping @dom96 in case he can tell you something about the debug story or what low-level stuff people do in it. It’s in a state of constant change where I’d probably have missed stuff. Main page did say it at least outputs a stack trace when it crashes.
I know it’s has several features for metaprogramming. That gives it lots of power. Compiling to C with low-latency GC gives it broad applications. Given Oberon was used for OS’s, you could probably build most things with it so long as performance wasn’t too bad. It probably has an unsafe keyword or something for high-performance, manual management of memory for when GC can’t be tolerated. You still get benefits of a HLL in that case. If resource-constrained, you might try embedding something like C in as a DSL. Ivory language does the latter in Haskell. Techniques like Design-by-Contract help a lot in debugging. You should aim to knock out as many errors as possible ahead of time with a safe, high-level language that localizes them where possible. Then, contract assertions can help you find the rest esp combined with property-based testing or fuzzing.
Far as self-compiling, Nim is already implemented in Nim. It leverages C for performance. One can always build subsets that compile unoptimized to x86 if they want to compile something in itself. This is something many obsess about but that isn’t that important. That’s because traits we judge compilers on are same as a program: compilation speed, runtime speed, flexibility, safety/security. The compilers that produce fastest code are black boxes in unsafe languages you’d have to invest massive time into understanding. Those you can bootstrap and understand easiest will produce sub-optimal code you’ll likely not use. The last point is one should always use best tool for the job. Certain languages, like Ocaml, have features that make them inherently better at writing compilers balancing code size, safety, and performance. For extreme example, Ometa can implement things like JavaScript in a few hundred lines of code. Its tradeoff is conciseness and flexibility you’re not getting in C but way, way slower. Tradeoffs abound.
If you need both, then the Wirth languages and some Schemes are probably best. One has whole books telling you how to compile it with source code. Use them for trust/understanding or extract to C to leverage black boxes. For Scheme, you can use the easiest features to implement in a modern Scheme (eg Racket or Chicken) where you can use their optimizing compiler or extend a mini-homebrew Scheme you understand up to their level.
Those are the basic principles. Compiling in self just isn’t that important for 99.9+% of people’s use cases. It’s more a tradition of testing the language on something hard that the developers are sure will both stick around and get bigger. It’s not necessary, though, like with all the HLL’s written in C++ or whatever.
Thanks for the detailed reply! If you get ahold of them, I’d be much more interested to know how they intend to spin up the live environment, especially for something like the Squeak debugger. Even a simple curses or command line version (or curses) would be interesting if it can be made without a lot of code.
Nim does look like it has a number of nice properties and at least claims to aim for speed and allows manual GC. On the other hand, it does not seem to be very focused on its debugging tools. (Someone tried to remedy this)[https://forum.nim-lang.org/t/3076] and it does have source and line number info but presumably I can’t easily evaluate nim expressions and make calls in gdb (or Nim’s debugger if it had one).
Assertions are used a bit; maybe I should do more. But I’d really rather have a better debugger/repl/live environment (preferably written in the new language itself). Part of the reason is that the spec changes a lot as I explore and a lot is even scrapped. In this case, the language and (core) library also changes so updating assertions and test is rather costly. The same reason applies to other techniques that needs a lot of time before I can run the program and visualize the result to decide if the program should be scrapped or not (having complete correctness is usually not needed to visualize).
Is there any way to gain certainty more quickly?
I briefly looked at Nim’s compiler in Nim but it says that it was almost directly translated from Pascal and it is also quite large.
I didn’t understand what you mean here. Subset of Nim, the new language or the Nim compiler?
I think the original Ometa is also hard to debug.
Is it really that much slower? I thought you could use it for optimization using Ometa AST transformations.
In Scheme flavours, is it easy to implement specific behaviours with respect to memory layout and computation? Do you only get a language equivalent to what you specified or can you get the same amount of control as assembly and C (and I guess without fighting the language too much)?