7 ms·
The Neat Programming Language
- FeepingCreature 3y agoI too have made a language! Hope you find Neat interesting. It's heavily inspired by D, except with reference counting instead of garbage collector. Also, it has macros. My DConf talk on the language is now up too! https://www.youtube.com/watch?v=nDqlYnS-K2c https://www.youtube.com/watch?v=nDqlYnS-K2c
- jqpabc123 3y agoLooks fun ... and neat. But the mere fact that the primary ascribed goal is "fun" suggests it is not be taken seriously for actual work projects.
- FeepingCreature 3y agoOh no good god, Neat is definitely beta. This language has one user at the moment. Do not use it for actual work projects. (Though I will be proper chuffed if you do. I just can not recommend it.) To be honest, my goal is to have maybe 10 to 50 users right now. That'd be quite enough for this christmas.
- dr_kiszonka 3y agoRoR was optimized for developer happiness. Maybe a language optimized for fun could be used for serious work too? https://rubyonrails.org/doctrine https://rubyonrails.org/doctrine
- 7thaccount 3y agoThere's a few languages that claim to be optimized for fun (among other things). One example is Raku. I think -o Fun, is written somewhere.
- brabel 3y agoHaven't had the time to watch that talk yet... but what's your motivation to create a new language? Any drawbacks in D that you're trying to fix (besides the GC being replaced with RC - which many consider to also be GC by the way)?
- FeepingCreature 3y agoYep, here's the big ones: - proper macros - proper sumtypes with implicit conversion - more normal lambdas (seriously, D's lambdas are wild) - faster compiler with more caching and (maybe) eventually live reload? - proper packages instead of include path. And a bunch of small fry like format strings and named parameters. And yeah, um, GC can be good but the D GC kind of isn't. We use D at work, and we run into GC issues frequently. Neat's RC is a much thinner wrapper around C memory allocation, which is just better imo, much as GC and RC are logically equivalent in some ways.
- dataangel 3y agoFor the uninitiated what are wild about D lambdas? I'm familiar with C++ lambdas and Python lambdas.
- FeepingCreature 3y agoOkay so if you're calling a D std.algorithm function, for instance int factor = 2; assert([2, 3].map!(a => a * factor).array == [4, 6]); Then the `!` indicates that you're actually passing the lambda as a compiletime parameter to `map`. But the lambda can access the surrounding context! How does it pass a runtime value at compile time? So what you're passing is actually purely a function symbol. The way that it gets the stack reference to the surrounding function is that it's actually a nested function. And the way that `map` gets the stack reference to pass to the lambda is that, effectively, that instance of `map` is also a nested function of the calling function. That's also why you cannot pass a lambda as a template parameter to a class method in D: it already has a context parameter, ie. the class reference. In Neat, the value of the lambda is the stackframe reference, and it's just passed as a regular parameter: int factor = 2; assert([2, 3].map(a => a * factor).array == [4, 6]); Which avoids this whole issue at the cost of requiring some cleverness with refcounting.
- logicprog 3y agoHey, I've been trying to not interact on social media much anymore but I just wanted to let you know that this looks like a pretty great (neat :)) little language! I'm very comfortable using Rust so I'm probably not going to end up using it, but my girlfriend is a data scientist who only knows Python and recently tried to start programming a game involving nbody simulation in Python, only to find that for the type of games she was trying to make Python was far too slow, and she's been really struggling to find a language that's relatively close to systems programming level (since she doesn't want to use an established engine) that also uses concepts and paradigms she understands and has reasonable syntax and stuff, and this looks like exactly what she's been looking for! When I sent her the link she got extremely excited, even more so when she saw that you had examples that were making games and using sdl. I hope you keep working on this language because I think it really does hit a nice (if niche) sweet spot! As a funny side note, I read the part on your website where you talk about why you chose reference counting over garbage collection, and despite your light-hearted resentment of the fact that garbage collection is such a turn off for people, the fact that your language doesn't use garbage collection is one of the major factors in why she might end up using it lol. We decided not to do C# (my first suggestion actually) because since she doesn't want to use an engine, it wouldn't be used just as a scripting language, but for the entire object model and update loop and so on, so GC would be a dealbreaker. Sorry- (tiny rant mode as a hobbyist gamedev myself) there is actually a good reason for this -- it is much easier to do multithreading when you don't have a separate thread going over all shared memory, and it's much easier to do a soft real time thing like game development if your memory allocation and deallocation stays relatively consistent and predictable, even if it is slower overall. Also, yes malloc may cause variable slowdowns too, but definitely to a smaller degree/varience than the average garbage collector and more predictably since you can control when allocations happen; otherwise there wouldn't be a noticeable difference between using garbage collected and non garbage collected languages for writing large-scale games. Plus, in any case, that's the reason why game developers tend to try to limit dynamic allocation at runtime in the first place.
- FeepingCreature 3y agoDid she find the built-in vector types yet? :) Small-scale gamedev is one place where I think this language could shine. Anyway, please also tell her to hit up the Discord (or IRC) for any questions!
- mhh__ 3y agoA pleasant surprise seeing it on the frontpage.
- FeepingCreature 3y agoYeah I think I'dve been crushed if this had ended up at like three upvotes, lol.
- AndruLuvisi 3y agoThank you for sharing! I didn't notice any mention of tail calls. Does it support proper tail calls?
- FeepingCreature 3y agoNot as an enforced thing. To be honest, I'm personally not too into recursion as a "must have" feature.
- nsajko 3y agoHow is it implemented? Via transpilation to C?
- FeepingCreature 3y agoTwo backends: - LLVM is a proper SSA backend, it just uses the llvm-c API to generate bc modules, like any other LLVM-based compiler. It technically links with clang, but that's just cause I didn't want to set up my own link driver and optimization pipeline. - GCC is technically "transpiling" to C, but the generated C files are unreadable, because it's still SSA. The GCC backend is also used to build the releases. I just build the compiler with the GCC backend, then zip up all the .c files that it produced. That's why the release build script starts by building a bunch of C files.
- zem 3y agoI perked up at "like D but with built in sum types". is C interop intended to be as good as D's?
- FeepingCreature 3y agoIt runs on plain C ABI, so you can just define C functions as `extern(C)`, just as you would in D. But you can also use `std.macro.cimport` to import C headers directly. Check out the Dragon demo, https://github.com/Neat-Lang/neat/blob/master/demos/dragon.nt https://github.com/Neat-Lang/neat/blob/master/demos/dragon.n... : macro import std.macro.cimport; import c_header("raylib.h"); And then you can just use (most of) the Raylib functions and types. cimport is a massive hack: it runs gcc on the header in preprocessor expansion mode, then parses the result. Its tactic for C syntax it doesn't understand is "just skip it and hope for the best." :) Works surprisingly well. On x86, there was a lot to be gained from having a language specific ABI, because the cdecl ABI was so slow. But the x86-64 ABI, much as I may dislike its complexity, is genuinely plenty fast already.
- sillysaurusx 3y agofeep’s been working on this for like a decade. Happy to see it get some recognition.
- FeepingCreature 3y agoAh! Technically, this version of the compiler has no continuity with the first one. It's just named Neat too, because I spent like a day trying to think of another name after I got sniped on C*, and couldn't come up with anything, and went "hey, I already have a name." Neat as in the current iteration of the language, started in 2020.
- sillysaurusx 3y agoIs it fair to discount all the false starts and early work prior to 2020? A bit like saying Picasso has been working on a painting for a month instead of his whole life. I guess it’s more accurate to say that you’ve been an inveterate language designer for a very long time, and it’s nice to see your latest one go public.
- FeepingCreature 3y agoYay!
- cornholio 3y agoCould a Rust like borrow checker automatically and transparently insert the refcount logic only when it's needed, slowing down only access to variables with multiple owners, which would be a minority? And perhaps declare some special variables as "resource" to inform the compiler to really enforce the single write owner rule, for "special" external things like file descriptors etc. IMHO, memory as a tightly managed resource is adequate for system programming, but a hindrance in most other general programming tasks. But the approach has its point for actual resources any program needs to manage, allowing powerful compile time checks. So make it optional, with the default being automatic memory management.
- FeepingCreature 3y agoI'm coming from a D perspective, where you really don't want to enforce anything single-owner-like, because it makes algorithms awkward. Neat does some commonsense optimizations to avoid pointless refcount changes, for instance it internally assumes that all function parameters are managed by the caller and doesn't take an additional reference for them. But at the end of the day, it mostly just refcounts and calls it good enough. You can define types in Neat that are noncopyable, but it really limits what you can do with them. For instance, nested function references are noncopyable by default to avoid forcing closure allocations.
- beagle3 3y agoYou’ve basically described Nim’s memory management.
- shoozza 3y agoI think lobster does this. "Compile time reference counting / lifetime analysis / borrow checker."[1] "Reference Counting with cycle detection at exit, 95% of reference count ops removed at compile time thanks to lifetime analysis."[1] [1] https://strlen.com/lobster/ https://strlen.com/lobster/
- dang 3y agoRelated: Breakelse: When Compiler Developers Get Bored - https://news.ycombinator.com/item?id=37887426 https://news.ycombinator.com/item?id=37887426 - Oct 2023 (78 comments)
- FeepingCreature 3y agoI've been working myself up to this post for a while.
- IshKebab 3y ago> I’ve been in the D community for a decade and a half. People keep telling us they don’t like the GC. They keep saying GC is a dealbreaker. They want predictable memory usage and cleanup times. Somehow, none of this is ever an issue with C# and Java. Somehow, the fact that any memory allocator, including glibc’s, can incur arbitrary delays never matters. Well, fine! Fine. Whatever. I disagree with the choice, but as an offshoot of an offshoot, I really can’t afford alienating folks. I’m tired of arguing this point. Nobody has ever said that reference counting was a dealbreaker. So reference counting it is. I think this is a bit off. It is an issue with Java and C#, but they don't position themselves as a C++ replacement in the same way D does. Even so you still get lots of complaints about missing RAII and GC pauses (witness how well received Go's low latency pauses have been received). And yes glibc could introduce arbitrary delays, but it generally doesn't. Allocation is way faster than GC. Maybe he was talking about long pauses when you destroy a big C++ object (e.g. a big `std::map`)? That can definitely cause annoying big pauses due to deallocation. But he already identified the critical factor - it's predictable. You can fix it deterministically. Anyway reference counting is a decent choice. It can be very fast (especially if you are only referencing counting big objects and not little integers etc.)
- FeepingCreature 3y agoI've come around more to "yeah GC is a problem actually" since I wrote that snippet. It really depends on your usecase though.
- IshKebab 3y agoYeah I agree. There are plenty of use cases where it isn't a problem, but in those cases you would probably not pick D/C++/Rust/Zig anyway. Or you don't have to at least (I still use Rust in cases where a GC is fine because it's such a great language).
- deleted 3y ago[deleted]
- dataangel 3y ago> glibc could introduce arbitrary delays, but it generally doesn't. Yeah it does, that's why gamedevs and hfts don't use any malloc in the fast path, glibc or otherwise.
- dianeb 3y agoOnce upon a time, NCR's mainframe language was called Neat (the version I briefly used was called Neat/3 which was a low-to-mid-level language if memory serves. I was writing compilers.) My memories of a language with that name are unpleasant. This is probably the least useful of all comments, but please think of another name. My quibble with the language has to do with the use of '[' .. ']' pairs. I'm not confident that refactoring will be straight-forward. I could be wrong.
- FeepingCreature 3y agoI tried, I failed, now it's too late. Just gonna hope not many people remember that other language. Not sure what you mean with `[]`?
- dianeb 3y agoThis construct: string longestLine = [ argmax(line.strip.length) line for line in text.split("\n")]; print(longestLine); looks problematic to me -- are the brackets indicating scope? an array? something else? As far as the naming is concerned, you'll probably have to put up with remarks such as "neat code is messy" because that's the way people are with something new. Don't let that dishearten you!
- AeroNotix 3y agoIt looks like what python calls a list comprehension. An expression which builds up each element of a sequence, potentially from another sequence.
- FeepingCreature 3y agoYeah that's a macro (`std.macro.listcomprehension`), you can copy that file and change it to be whatever syntax you like. It's indeed inspired by Python's syntax.
- deleted 3y ago[deleted]
- fyzix 3y agoIs it ready for benchmarking? D currently sits at the top of https://github.com/jinyus/related_post_gen https://github.com/jinyus/related_post_gen and it would be interesting to see how neat stacks up.
- FeepingCreature 3y agoAh, I remember I looked at that. https://gist.github.com/FeepingCreature/cd935a209fa2eebd5928c012072ac52b https://gist.github.com/FeepingCreature/cd935a209fa2eebd5928... Here's a Neat port I had lying around, I think it's pretty much a copypaste from D? I think it was a bit slower, can't remember why though. Something like 80ms vs 60ms with `-O -release`.
- bhaney 3y ago> The most important thing to me is that you should have fun using Neat. The biggest innovative new language in C-likes, Rust, is primarily built on not letting you do things. Nobody understands what the hell C++ is doing anymore, it’s approaching string theory levels of opaqueness. Go is specifically built on the premise that Google developers are incompetent and must be kept away from any semblance of language power. I can tell I'm gonna enjoy Neat. The dev understands perfectly what I like and dislike in a language.
- voidfunc 3y agoI got a chuckle. I'm amazingly productive in Go but I would love something with less training wheels that's not C++ complex or Rust.
- bhaney 3y ago> Some languages solve this problem by allowing every value to be null, or nil, or None. Those languages are bad and after this paragraph I will speak no more of them It keeps getting better
- mlinksva 3y agoNeat : Nice :: D : Java ~1.3 Well, the names (which I enjoy) are similar anyway, that's what made me think of the no doubt overstated analogy.