Yes, code blocks in Org are executable, but I was aiming for simple embedding and zero build-time, thus conservative choice of separating README and the actual header.
I was hoping to see a “this is just for fun” disclaimer but didn’t see one. Please never actually use this in a project that other people will have to read or contribute to.
Picking out an obvious define function that compares a float with a float sum of that nature should indicate an good understanding of why that might be called wizardry and deserving of a second look.
Hats off to the peer comment that suggested scaling against epsilon rather than simpliy regurging the substitution "as was" from the header.
The scaling is better in general, optional in some specific contexts.
it uses absolute difference epsilon equality ('close enough to be considered equal'):
static int pretty_float_equal (float a, float b) { return fabsf(a - b) < FLT_EPSILON; }
static int pretty_double_equal (double a, double b) { return fabs(a - b) < DBL_EPSILON; }
static int pretty_long_double_equal (long double a, long double b) { return fabsl(a - b) < LDBL_EPSILON; }
This code is incorrect, but I don't blame them. :) Probably one of the most common float-related mistakes, even among people who "know how floats work".
FLT_EPSILON is the difference between 1.0 and the next larger float. It's impossible for numbers less than -2.0 or greater than 2.0 to have a difference of FLT_EPSILON, they're spaced too far apart.
You really want the acceptable error margin to be relative to the size of the two numbers you're comparing.
Also, everyone should read the paper "What, if anything, is epsilon?" by Tom7
I would go even further and say that any equality comparison of float numbers has to be a one-off special case. You need to know how much error can arise in your calculations, and you need to know how far apart legitimately different numbers will for your particular data. And of course the former has to be smaller than the latter.
Indeed, FLT_EPSILON is not a one-size-fits-all solution, but it's good enough for frequent case of comparing big enough numbers, which is not covered by regular ==. So it's a convenience/correctness trade-off I'm ready to make.
If the numbers you are comparing are greater than 2, abs(a - b) < FLT_EPSILON is equivalent to a == b. Because it's not possible for two large numbers to not be equal, but also closer together than FLT_EPSILON.
But it's impossible to have a number that's 0.00000011920929 less than 5.0, or 0.00000011920929 more than 5.0, because the floats with enough magnitude to represent 5 are spaced further apart than that. Only numbers with magnitude < 2 are spaced close enough together.
In other words, the only 32-bit float that's within ±0.00000011920929 of 5.0 is 5.0 itself.
> Deprecate Lua, Python, JavaScript, Ruby and a dozen other languages, because Pretty C is the ultimate scripting language, but lightning-fast and strongly typed!
Type names are nice; Perfect choice for the in-built func macros (like min); Len -- love it. Named boolean operators -- might be a bit much but go for it; Ternaries are illegible so you can only improve them; Not completely sold on all your loop definitions but some make sense to me; Resource tracking is impressive; The for... look a bit ugly -- could probably call it something else.
All in all: quite a solid attempt. I'll give you 8/10 for the design of this. The way you sketched this out in C using macros is really elegant. This actually looks like good code. Would I use it? It's a new language and I like C already. It could help people learn C and think about language design. Since the way you've done this is very clear.
Well, you don't have to use it all. My projects mostly use booleans, len(), max/min, and some operator aliases, because there wasn't much need for other tasty stuff yet. So give it a shot, even if for a couple of operator macros!
You can use the Boehm-Demers-Weiser GC with C. It's conservative, because it has to be with C, so it may/will miss things (it will treat integers etc. as potential pointers, and so avoid freeing anything "pointed to" by them), and so it works best as an extra layer of protection/leak detector, but it can be used as a replacement for freeing memory too.
Reminds me of a C++ codebase I once had to inspect that was entirely written as if it were written in Java. With camelcase naming for everything, getters and setters for every class variable, interfaces everywhere.
Also because the special characters were (and are) difficult to type on European keyboards.
Characters like []{}\|~ are behind multi-finger access and often not printed at all on the physical keys (at least in the past). You can see how this adds a hurdle to writing C…
Pascal was designed by a European, so he preferred keywords which could be typed on every international keyboard. C basically just used every symbol from 7-bit ASCII that happened to be on the keyboards in Bell Labs.
Just as example, on my slovenian QWERTZ layout: [ - altgr+f, ] - altgr+g, { - altgr+b, } - altgr+n, \ - altgr+q, | - altgr+w, ~ - altgr+1.
You get used to them, though you start feeling like a pianist after a short coding session. The one most annoying for me are the fancy javascript/typescript quotes, which I have to use all too often: ` - altgr+7.
There’s so many assumptions here about a person who’s starting to learn programming.
For starters, that they’re on Linux, they feel comfortable running complex CLI commands, they can memorize the U.S. layout just like that, and that they can type without looking at the physical keys (because changing the virtual mapping means keys produce something else than what the label says).
In reality, the learner’s first exposure to C family languages is more likely to be a website where you can run some JavaScript in a text box. And the first hurdle is to figure out how to even type {}. American developers just completely forget about that.
or maybe popular proglangs were designed for writing on USAn press/office keyboards – remember that UNIX came to be as a typesetting appliance — disregarding anyone else.
I’ve been writing C and its progeny (C++, JavaScript, Rust etc.) since 1990 on a Finnish keyboard.
The AltGr brackets are fine. The truly annoying character to type is the backtick (which is a quite new addition to the pantheon of special characters, C doesn’t use it).
My personal opinion is that Niklaus Wirth had the better overall ideas about clarity and inclusiveness in programming language design, but that battle is long lost. (What you consider the character set needed for "proper programming" is really a relatively new development, mid-1990s and later.)
Backticks were fairly important for shell scripting in the past, but have officially been replaced with $(), which can be nested.
My intuition is that Perl would be the most challenging on a keyboard where it's harder to type unusual punctuation, since it feels like a very punctuation-heavy language, but I don't know whether it actually uses more than C (I think the backtick has a shell-style meaning in Perl too).
Well unless opting for something like Dvorak, you are indeed doomed to something that was specificcaly designed to please typewriter mechanical constraints without much care for the resulting ergonomics.
I use a Bépo layout personally, on a Typematrix 2030 most of the time, as French is my native language.
I'm from a non-English country. I only ever use layout of my locale when I write in my language. That's how it was ever since I was a kid who knew little English. And that's how all computers I've encountered in my country are set up - English first, local second.
In addition, our layout, overwrites only the numerics – all other symbols are the same as on a US layout.
I tried switching to US a few times, but every time muscle memory made me give up soonish - especially since there are big benefits to using same keyboard layout as other people in your office are using.
Also practically everytime I need to write a comment, commit message or email I need my č, š and ž. It's kinda nice to have them only a single keypress away.
Spectacular?? Terrifying. If I need to type non-ASCII Latin characters I'll just use compose sequences. The thought of a non-U.S. keyboard layout with modifiers required to type []{}<> and so on is terrifying.
> camelcase naming for everything, getters and setters for every class variable, interfaces everywhere
This is not far off from the guidelines in many cases, e.g. Windows code (well, not every variable of course.) A lot of Java design was copied from C++.
I've seen similar codebases as well written by people who have spent way too much time with Java. One even had it's own String class which was just a wrapper for std::string with Java-like methods.
> The word "REPEAT" should not be used in place of "SAY AGAIN", especially in the vicinity of naval or other firing ranges, as "REPEAT" is an artillery proword defined in ACP 125 U.S. Supp-2(A) with the wholly different meaning of "request for the same volume of fire to be fired again with or without corrections or changes" (e.g., at the same coordinates as the previous round).
Given the idea behind this repo is to cause pain, why not add a shebang to your file [0] to make it executable.
I saw a blog post a long time ago that went into the details of how ./foo worked, and how it executed an elf file. You could register `.c` programs in the same way to be compiled and run?
Now I have a very evil idea: what about registering a binfmt handler for the header bytes “#include”? Sure, it doesn’t handle all C/C++ programs (notably any program that dares to start with a comment), but it would not require modifying any source code!
(For even more insanity I guess you could also trigger on // and /*, although there’s some risk of false positives then!)
generally they aren't, as scripting usually implies an interpreter, though no one is stopping you from using a wrapping script that quietly compiles on first run and caches a bunch of executables somewhere. not much different than python producing bytecode files as it goes along.
Script usually implies some kind of task that runs once and the exits. As opposed to a system that is expected to run indefinitely.
There are good reasons for why scripts are often interpreted and why systems are often compiled, but that's not what defines them. There are definitely scripts that are compiled and systems that are interpreted out in the wild.
the original definition is likely tossing shell commands in a file to run later. chaining commands together. since perl and python supplanted this, they get lumped in as 'scripting languages'. both certainly can be used to write long running systems or short one off tasks.
compiled languages are rarely used for one offs because the effort they require is usually greater than the task calls for.
a big part of perl/python use is in tying together libraries written in more difficult lower level compiled languages.
you'll also see scripting used to refer to languages embedded in larger projects. lua scripts to control entities in a game, for instance. do they compile these somehow? I never did in the little project I used lua for.
----
all of that together, I expect that scripting as a concept largely boils down to conceptually simpler languages with less view of the ugly underbelly of how things actually work in a computer, used to chain together abstractions created by lower level code.
scripting is duct-tape. whether you duct-tape together a one-off task or some wad of long running functionality is besides the point.
> you'll also see scripting used to refer to languages embedded in larger projects.
Yes, but this is conceptually exactly the same as the aforementioned shell scenario. This is not something different.
Just as I suspected, there is only one definition, and one that has proven to actually be well defined to boot as you managed to reiterate the only definition I have ever known to perfection.
Sure, there is no "rule" against it. But words/phrases have commonly-accepted meanings and willfully ignoring or appropriating those meanings implies either cultural ignorance or a concealed agenda.
If you want to insist that scripting languages can be either compiled or interpreted, then its better to just drop it altogether and just say "language" because the "scripting" part has utterly lost its identity at that point.
Can someone clarify whether this is intended as a joke or whether the author is actually confused? I mean, nothing about this makes sense: it's not "scripting"; it claims to introduce "strong typing" while it does nothing about typing; it introduces all kinds of operator aliases "modeled after Lua and Lisp" that are present in neither of these languages. But it's not an obvious parody either, so I'm genuinely not sure.
I mean he has to be serious, right: "Deprecate Lua, Python, JavaScript, Ruby and a dozen other languages, because Pretty C is the ultimate scripting language, but lightning-fast and strongly typed!!"
With C23 (nullptr, auto typing, typeof) and C11 (generics) it got more guarantees and type-related primitives. You can still do void*, but you are strongly discouraged from it.
#include "pretty.h"
void print_int(int value){
println(value);
}
int main (int argc, string argv[])
{
long value = 23849234723748234;
print_int(value);
}
How is this strongly typed?
$ cc test.c -o test && ./test
-1411401334
And to be clear, weak vs strong isn't a boolean property but a spectrum, but would be hard to argue with a straight face than C is a strongly typed language.
C is likely the only example of a programming language that is clearly statically typed while at the same time being weakly typed. For a reason: as your example shows, it's a really bad idea (but understandable for a language from the 60's).
I don't think Java is weakly type even in generics. You can't "fake" your way with types like in C, you need to explicitly cast, which fails if you try to make an invalid type cast.
C is strongly typed in some areas in that ISO C requires a diagnostic if you mistakenly use a struct foo * where a struct bar * is required.
It's weak in many areas, such as, oh, that you can implicitly convert an out-of-range floating point value to an integer type and get undefined behavior.
Linkage in C is not type safe. An extern int x declaration in one translation unit can be matched with an extern double x = 0.0 definition in another. Linkers for c typically accept that without a diagnotic: the program links.
225 comments
[ 3.5 ms ] story [ 215 ms ] thread> And it’s backwards-compatible with C and all of its libraries!
I can't wait to give it a shot! This looks like a riot.
I love the literate way you have explained your thought process in the readme.
> Everyone defines these, so why not provide them?
Honestly, that's fair.
Okay then.
I was hoping to see a “this is just for fun” disclaimer but didn’t see one. Please never actually use this in a project that other people will have to read or contribute to.
seems like its obvious to me that its a joke
how is this wizardry possible?
addendum: why are obviously rhetorical questions are taken so literally here?
Picking out an obvious define function that compares a float with a float sum of that nature should indicate an good understanding of why that might be called wizardry and deserving of a second look.
Hats off to the peer comment that suggested scaling against epsilon rather than simpliy regurging the substitution "as was" from the header.
The scaling is better in general, optional in some specific contexts.
FLT_EPSILON is the difference between 1.0 and the next larger float. It's impossible for numbers less than -2.0 or greater than 2.0 to have a difference of FLT_EPSILON, they're spaced too far apart.
You really want the acceptable error margin to be relative to the size of the two numbers you're comparing.
Also, everyone should read the paper "What, if anything, is epsilon?" by Tom7
But it's impossible to have a number that's 0.00000011920929 less than 5.0, or 0.00000011920929 more than 5.0, because the floats with enough magnitude to represent 5 are spaced further apart than that. Only numbers with magnitude < 2 are spaced close enough together.
In other words, the only 32-bit float that's within ±0.00000011920929 of 5.0 is 5.0 itself.
Gotta research now where the 0.00000011920929 number comes from...
Is it representable as a non-trivial ratio of integers?
https://www.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh
I have wondered why we have case/esac, if/fi but while/done. I imagine the author himself figured that while/elihw would just be entirely ridiculous.
With the reverse-keyword convention we'd get "od", not "elihw", though.
The 'od' utility already existed, apparently, so Bourne opted for "done".[edit: typos]
Umm… that’s quite the goal.
I’ll stick with deprecated Python.
All in all: quite a solid attempt. I'll give you 8/10 for the design of this. The way you sketched this out in C using macros is really elegant. This actually looks like good code. Would I use it? It's a new language and I like C already. It could help people learn C and think about language design. Since the way you've done this is very clear.
https://learn.microsoft.com/en-us/cpp/cpp/property-cpp?view=...
Before he wrote the Bourne shell the author wrote an ALGOL compiler, and ALGOL inspired Bourne syntax:
https://en.wikipedia.org/wiki/ALGOL_68C
I think in Europe C was not as common as other languages at the time so the terseness looked odd.
Characters like []{}\|~ are behind multi-finger access and often not printed at all on the physical keys (at least in the past). You can see how this adds a hurdle to writing C…
Pascal was designed by a European, so he preferred keywords which could be typed on every international keyboard. C basically just used every symbol from 7-bit ASCII that happened to be on the keyboards in Bell Labs.
You get used to them, though you start feeling like a pianist after a short coding session. The one most annoying for me are the fancy javascript/typescript quotes, which I have to use all too often: ` - altgr+7.
For starters, that they’re on Linux, they feel comfortable running complex CLI commands, they can memorize the U.S. layout just like that, and that they can type without looking at the physical keys (because changing the virtual mapping means keys produce something else than what the label says).
In reality, the learner’s first exposure to C family languages is more likely to be a website where you can run some JavaScript in a text box. And the first hurdle is to figure out how to even type {}. American developers just completely forget about that.
It's looks like being deliberately designed for press/office usage and not for proper programming.
The AltGr brackets are fine. The truly annoying character to type is the backtick (which is a quite new addition to the pantheon of special characters, C doesn’t use it).
My personal opinion is that Niklaus Wirth had the better overall ideas about clarity and inclusiveness in programming language design, but that battle is long lost. (What you consider the character set needed for "proper programming" is really a relatively new development, mid-1990s and later.)
My intuition is that Perl would be the most challenging on a keyboard where it's harder to type unusual punctuation, since it feels like a very punctuation-heavy language, but I don't know whether it actually uses more than C (I think the backtick has a shell-style meaning in Perl too).
Well unless opting for something like Dvorak, you are indeed doomed to something that was specificcaly designed to please typewriter mechanical constraints without much care for the resulting ergonomics.
I use a Bépo layout personally, on a Typematrix 2030 most of the time, as French is my native language.
In addition, our layout, overwrites only the numerics – all other symbols are the same as on a US layout.
Also practically everytime I need to write a comment, commit message or email I need my č, š and ž. It's kinda nice to have them only a single keypress away.
wow.
thanks for this gem.
This is not far off from the guidelines in many cases, e.g. Windows code (well, not every variable of course.) A lot of Java design was copied from C++.
The actual name of the repo is "pretty.c", but the name used for the language/dialect/result/horrorshow[*] is "Pretty C".
The actual code file you include is called "pretty.h", which makes sense since it's a header, of course. Confusing!
Edit: escapes.
[*] Yes, I'm a C programmer, currently hunting for black-marked insulin to combat the rapid-onset diabetic attack from all that sugar. Sorta.
> The goals for Pretty C are: Provide so much syntactic sugar as to cause any C developer a diabetes-induced heart attack.
"unless" seems more readable than "ifnt".
I've seen "loop" in other languages. But Qt calls it "forever", and that is indeed very pretty. Very Qt, even
You can break a "forever" loop so I think "loop" is a better name.
> The word "REPEAT" should not be used in place of "SAY AGAIN", especially in the vicinity of naval or other firing ranges, as "REPEAT" is an artillery proword defined in ACP 125 U.S. Supp-2(A) with the wholly different meaning of "request for the same volume of fire to be fired again with or without corrections or changes" (e.g., at the same coordinates as the previous round).
https://en.wikipedia.org/wiki/Procedure_word#Say_again
More seriously, PASCAL has repeat-until loops, similar to do-while loops in C.
Oh shit wait, you're John Tromp, BLC creator! I'm a fan!
[0] https://bellard.org/tcc/tcc-doc.html
I saw a blog post a long time ago that went into the details of how ./foo worked, and how it executed an elf file. You could register `.c` programs in the same way to be compiled and run?
[0] https://gist.github.com/jdarpinian/1952a58b823222627cc1a8b83...
(For even more insanity I guess you could also trigger on // and /*, although there’s some risk of false positives then!)
There are good reasons for why scripts are often interpreted and why systems are often compiled, but that's not what defines them. There are definitely scripts that are compiled and systems that are interpreted out in the wild.
compiled languages are rarely used for one offs because the effort they require is usually greater than the task calls for.
a big part of perl/python use is in tying together libraries written in more difficult lower level compiled languages.
you'll also see scripting used to refer to languages embedded in larger projects. lua scripts to control entities in a game, for instance. do they compile these somehow? I never did in the little project I used lua for.
----
all of that together, I expect that scripting as a concept largely boils down to conceptually simpler languages with less view of the ugly underbelly of how things actually work in a computer, used to chain together abstractions created by lower level code.
scripting is duct-tape. whether you duct-tape together a one-off task or some wad of long running functionality is besides the point.
Yes, but this is conceptually exactly the same as the aforementioned shell scenario. This is not something different.
Just as I suspected, there is only one definition, and one that has proven to actually be well defined to boot as you managed to reiterate the only definition I have ever known to perfection.
If you want to insist that scripting languages can be either compiled or interpreted, then its better to just drop it altogether and just say "language" because the "scripting" part has utterly lost its identity at that point.
Java is weakly typed in its generics, despite being statically typed. I’m sure there are more examples.
It's weak in many areas, such as, oh, that you can implicitly convert an out-of-range floating point value to an integer type and get undefined behavior.
Linkage in C is not type safe. An extern int x declaration in one translation unit can be matched with an extern double x = 0.0 definition in another. Linkers for c typically accept that without a diagnotic: the program links.