12 ms·
Aaaa, people are doing a lot of C alternatives lately, I should really make a post for my C alternative before the field is completely saturated... But it's not
by FeepingCreature 5y ago
Aaaa, people are doing a lot of C alternatives lately, I should really make a post for my C alternative before the field is completely saturated... But it's not ready yet :(
I kind of want to at least get hashmaps in before I go public.
Re C3, I think the README could do with more sample code? Not exactly "Hello World", but something to get you hyped about using it.
Things I like:
- macros! macros are fucking awesome. giv examples
- modules are just a straight win
- built-in dynamic arrays, yess.
- compile-time execution is kind of a precondition for macros. Hopefully the same system.
- I've always wanted to play with generic modules. `import foo!(int) as IntFoo;` It seems a logical extension.
- Result-based error handling for the win. Though it really depends on language support how straightforward this is; it can easily degenerate into very spammy error handling. Definitely would like to see examples of this.
- Built-in strings: hopefully UTF-8!
- No preprocessor. Heck yes, it's a crutch.
- Pre/postconditions are nice, but they make a lot of mess on inheritance.
- Immutability by default is definitely a win.
Things you should totally steal from my language: :)
- Format strings are just nice.
- Packages as a generalization of modules: a package is a folder in the same way a module is a file. Dependencies between packages must be explicitly stated. This makes the build system's dependency tracking actually meaningful by effectively doing away with the global search path. I wish more languages would do this.
- D recently acquired automatic C header file import. (Neat has this as a macro.) I cannot overstate how useful this is for hitting the ground running.
- I don't know if your macro implementation has quasiquoting (it's hard to tell from the examples) but if not: add it. This makes macros immensely more convenient.
- slmjkdbtl 5y agointerested to know if you don't mind post it here
- FeepingCreature 5y agoSure, but keep in mind it's pre-pre-alpha and the current released version is kind of outdated: https://github.com/neat-lang/neat https://github.com/neat-lang/neat Because Neat is self-hosted and frequently depends on syntax features added a few commits ago, building from fresh source can take up to half an hour. You also need a D compiler (for the initial bootstrap version), but that comes with gcc nowadays. If you wanna try that, just run `bootstrap.sh`. (You may have to patch it to use gdc, not ldc, but the commandline should be the same.) This takes a while because it has to build the compiler something like 60 times, each with the previous version. Don't do that though! Gimme a ping and I'll slap a new release tag on it. The releases use the C backend to generate a C dump of the compiler, that can then be shipped and compiled on the target system. Neat is more a D-like than a C-like, but it only breaks C syntax in areas where I think C straight up made the wrong call, like the inside-out type syntax. Memory management uses automatic ref counting, with some optimizations to keep number of inc/dec manageable. The thing I'm most proud of is the full-powered macro system, which is really more of a compile-time compiler plugin system. Here's an example of using the C import macro to bind to a C library: https://github.com/Neat-Lang/neat/blob/master/demos/glfw.nt https://github.com/Neat-Lang/neat/blob/master/demos/glfw.nt Another good example of a macro would be listcomprehensions: https://github.com/Neat-Lang/neat/blob/master/src/neat/macros/listcomprehension.nt https://github.com/Neat-Lang/neat/blob/master/src/neat/macro... You can tell it's just compiler code that happens to be loaded at project compiletime. You can see listcomprehensions at work in the sparkline demo: https://github.com/Neat-Lang/sparkline/blob/master/src/sparkline.nt https://github.com/Neat-Lang/sparkline/blob/master/src/spark... `compiler.$expr xxx` is itself a macro, that parses an expression `xxx` and returns an expression that creates a syntax tree that, when compiled, is equivalent to having written `xxx`. It's effectively the opposite of `eval`. In that expression, `$identifier` is expanded to a variable reference to "identifier". So `ASTSymbol test = compiler.$expr $where && $test;` is equivalent to `ASTSymbol test = new ASTBinary("&&", where, test)`. (This shows its worth as expressions become more expansive.) All in all, this lets you write `bool b = [all a == 5 for a in array]`, and it's exactly equivalent to a plain for loop. You can see the exact for loop at line 103 in that file. `({ })` is stolen from gcc; google "statement expression". The one thing I'm still blocking on is hashmaps, once that's in I'll make a proper announcement post. And, of course, documentation. :-)
- lerno 5y agoBTW there's a discord for people working on low level programming languages: https://discord.gg/tgmUz9cFyv https://discord.gg/tgmUz9cFyv Maybe you find it interesting?
- jstimpfle 5y ago> Built-in strings: hopefully UTF-8! Hopefully just bytes, which trivially allows storage of UTF-8. > built-in dynamic arrays, yess. Dynamic arrays are only few lines to implement. There are different ways to do them, and no matter how, there will always be some problematic aspects. Not sure why you would want to choose one particular implementation and elevate it to a higher status. > No preprocessor. Heck yes, it's a crutch. It's ugly and inexperienced users will write bugs using it, but it's also tremendously useful. You mentioned quasiquoting as an alternative, but I'm not positive that it works as a preprocessor replacement for a language that lacks the "homomiconity" of LISP. Are there examples that show it works?
- FeepingCreature 5y ago> Dynamic arrays are only few lines to implement. There are different ways to do them, and no matter how, there will always be some problematic aspects. Not sure why you would want to choose one particular implementation and elevate it to a higher status. Just having a default in the language is insanely useful. I can only appeal to experience here (with D, which has built-in arrays), but I never want to be without them again. This goes doubly for my language, where dynamic arrays are actually a bit involved due to the need for slices, refcounting and capacity tracking for the doubling strategy on append. Not something you want to reimplement everywhere. > It's ugly and inexperienced users will write bugs using it, but it's also tremendously useful. You mentioned quasiquoting as an alternative, but I'm not positive that it works as a preprocessor replacement for a language that lacks the "homomiconity" of LISP. Are there examples that show it works? As an example, something like #define SQUARE(X) ({ typeof(X) x = X; x * x; }) could in a language with macros (and a better function macro syntax than I have at the moment :p) be rewritten as macro SQUARE(X) ({ typeof($X) x = $X; x * x; }); Which has the exact same effect, but does not suffer from the C preprocessor problems caused by string/token interpolation. Also, errors can be easily and cleanly attributed to the actual location they occur, because SQUARE's nature is a parse tree, not a token list. As for macros like `#define BEGIN {`, I consider it an advantage that they don't work. :)
- jstimpfle 5y ago> refcounting and capacity tracking for the doubling strategy on append. Not something you want to reimplement everywhere. Not something I want everywhere, in the first place. Especially refcounting. > macro SQUARE(X) ({ typeof($X) x = $X; x * x; }); Yes, stuff like that works, but only if your replacement body is a fully formed syntactical expression. Such a "syntactical macro" system can be nice because it's safer, and it's applicable in _most_ cases. But not for all - there are many situations where the full generality of C preprocessor macros (which are lexical / "token list" macros as you say) are useful. X-macros might be the example that I use most, and that wouldn't work with syntactical macros. Another application are macros around for loops, like FOREACH_FOO(...) or SCOPED_LOCK(...) for example. All dirty hacks that I like to use from time to time and that I prefer immensely to systems built in to the programming language and lead to immense complications in the language. Another example would be partial lists of any kind, for example lists of compiler intrinsic attributes #define FOO_API __attribute__((what1)) __attribute__((what2)).. String interpolation is probably not in scope for a syntactical macro system either printf("Hello from " PROG_NAME " version: " PROG_VERSION "\n"); Another example, but you might sneeze at that one - my current project is a pile of macro hacks, it contains a lot of stuff like #define DEFINE_BUILTIN(bkind, bname, num_args) else if (is_identifier(t->t_name.buf, bname)) \ ... #define PREC(t, p) case t: prec = p; break; It's more "temporary" stuff that will need factoring into data tables where there are extensions, but a lot of it is just good enough and will never be touched again. -- Expecting code to be so clean, always from the beginning, that all macros one would need ever can always be defined by syntactically complete expression bodys, that is not going to work. Just like there have been many attempts at getting rid of text-based programming languages and moving to structured (syntactical) editors - that hasn't panned out either.
- 95014_refugee 5y agoUtf-8 is instant failure for embedded targets, and essentially guarantees the failure of any C replacement.
- flohofwoe 5y agoHuh? Please explain if you don't mind. UTF-8 is completely backward compatible with 7-bit ASCII, so if you don't need international characters everything remains exactly the same, and if you need international characters, strings are still just regular "bags of bytes". The only difference (for international strings) is that the number of bytes in the strings isn't the same as the number of characters (or rather UNICODE code points). But that's only relevant if you actually need to process a string down on the character level. Most CRT ASCII string functions work just fine on UTF-8 data, even strtok() if the delimiters are 7-bit ASCII.
- mhh__ 5y agoD's ImportC is actually a full C compiler (or will be). You're supposed to use it on header files, but the capability is there - it's not just a binding generator.
- FeepingCreature 5y agoYes, D's approach is a lot more general than mine. However, I believe that this is a case where 10% of effort unlocks 80% of value.
- lerno 5y ago- Format strings, are you thinking about string interpolation or? - A module isn't a file in C3 but that's a deep subject to get into. - Automatic C header file import is something Zig is also touting as a feature. This was something I thought I would want early on. But as I worked myself through examples I find that it's hard to get right in all cases, which means that you'll run into cases where your language "almost" works. Plus now you actually tied your language not only to the C ABI, but the entire C standard (note how headers will for example contain static inline code that you will need to parse, or macros that define aliases of functions and builtins). That said a tool to automatically extract a "best effort" interface is planned. - Regarding macros the difficulty has been to balance power with readability. So that is something which I am considering but still haven't quite embraced. Instead I have macros taking unevaluated expressions and you can in compile time get different things, e.g. `$offsetof("Foo", "a")` will give you the offset of the member `a` in the type `Foo`, but it's done through a special function rather than allowing straight up string interpolation. We'll see once the standard library work starts for real.