Top
Best
New

Posted by synergy20 10/24/2024

Pretty.c(github.com)
370 points | 221 commentspage 3
pineaux 10/24/2024|
I feel compelled to try it out in a serious way and contribute to it. I have strong knowledge of python and am learning C. Are there good reasons -apart from attracting the ire of c-programmers- to not use it?
aartaka 10/24/2024|
Author here. I'll be glad to accept any contribution that makes C more readable, so PR away!
bitwize 10/24/2024||
I'm reminded of the guy who did

    #define BEGIN {
    #define END }
and a whole bunch of other macro-larkey just to make C look more like Pascal. Only then would he deign to code in it.
epcoa 10/24/2024|
https://en.wikipedia.org/wiki/Stephen_R._Bourne

https://www.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh

sph 10/24/2024||
Now that's just silly. And I see the backwards keyword terminators (LOOP/POOL).

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.

giucal 10/24/2024|||
> I have wondered why we have case/esac, if/fi but while/done.

With the reverse-keyword convention we'd get "od", not "elihw", though.

    while ...    for ...
    do           do
        ...          ...
    od           od
The 'od' utility already existed, apparently, so Bourne opted for "done".

[edit: typos]

epcoa 10/25/2024||
No, there’s OD. DONE is different but no less perverse.
pavlov 10/24/2024|||
Just call it wyl/lyw. Pronunciation maintained, problem solved.
Validark 10/24/2024||
Evil, yet beautiful. Hats off to you.
aartaka 10/24/2024|
Thanks!
throwaway19972 10/24/2024||
Does "strong typing" now just mean "static typing"? Afaik both lua and python are already strongly typed. Javascript is not and I have no clue about ruby.
lelanthran 10/24/2024|
> Does "strong typing" now just mean "static typing"?

The distinction strong and weak typing is irrelevant in practice.

Weak (but present) static typing beats strong dynamic typing every single time, because what is valuable is NOT "Do I see a type mismatch error only when a user accesses it?", it's "does this mismatch prevent a deployment?"

IOW, the only distinction in production is dynamic typing vs static typing, not strong typing vs weak typing.

throwaway19972 10/24/2024|||
> because what is valuable is NOT "Do I see a type mismatch error only when a user accesses it?", it's "does this mismatch prevent a deployment?"

I argue that understanding the semantics clearly and unambiguously is the most relevant thing, and strong typing tends to do that better (imho—with my only other examples being javascript and the "stringly-typed" perl and some random irrelevant BASIC dialects).

> Weak (but present) static typing beats strong dynamic typing every single time,

Can you give me an example? I don't think I've ever heard of such a thing. The closest I can think to this is maybe, arguably, the use of `void*` pointers in C which is difficult to see as anything other than pragmatism rather than some deeply beneficial way to write code—even explicit casts produce much more readable code. Another argument I could see is for operator overloading, which (IMO) produces much less readable code, or the implicit conversions feature of Scala (which also, IMO, produces less readable code, but they've addressed a lot of the major problems with it).

syrrim 10/24/2024|||
recommend setting up tests for your code s/t failures block deployment. can catch categories of bugs beyond typing errors
ipsum2 10/24/2024||
`equal(0.3, 0.2 + 0.1); // true`

how is this wizardry possible?

masklinn 10/24/2024||
It uses type dispatch to perform an epsilon comparison:

    static int pretty_float_equal (float a, float b) { return fabsf(a - b) < FLT_EPSILON; }
So it’s https://docs.python.org/library/math.html#math.isclose
hmry 10/24/2024||
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

im3w1l 10/24/2024|||
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.
aartaka 10/24/2024|||
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.
hmry 10/24/2024||
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.
jdthedisciple 10/24/2024||
what

    // FLT_EPSILON == 0.01
    equal(4.999, 5); // true
    4.999 == 5;      // false
am i missing?
hmry 10/24/2024||
FLT_EPSILON is not 0.01, it's 0.00000011920929.

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.

jdthedisciple 10/24/2024||
Oh you're right, thanks for the explanation!

Gotta research now where the 0.00000011920929 number comes from...

masklinn 10/24/2024||
It's the distance between 1.0 and the next representable float.
jdthedisciple 10/24/2024||
I got that but I am curious how to derive that number.

Is it representable as a non-trivial ratio of integers?

hmry 10/24/2024||
Good question! It's 1/(2**23), because 32 bit floats have 23 bits after the decimal point
defrost 10/24/2024|||
Is What Every Computer Scientist Should Know About Floating-Point Arithmetic wrong ??!!

addendum: why are obviously rhetorical questions are taken so literally here?

leansensei 10/24/2024|||
Because text doesn't convey sarcastic voice tonality, so the intent is far from obvious.
defrost 10/24/2024||
Sarcastic? Okay, if you say so.

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.

ipsum2 10/24/2024|||
It's meant as both humorous and a nerd snipe :)
slimsag 10/24/2024|||
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; }
vbezhenar 10/24/2024||
This is wrong code. It only works somewhat correctly when a and b around 1.
stabbles 10/24/2024|||
Yeah, should be scaled like |x - y| <= ε * max(|x|, |y|)
aartaka 10/24/2024||
Will do.
jffhn 10/24/2024||
If both terms are infinites and of same sign, subtraction will give NaN and it will fail.
listeria 10/24/2024||
How is that a problem? infinities shouldn't be considered equal
jffhn 10/24/2024||
For IEEE 754, and in Java for example, they are. Only NaN is not equal to itself (and different from itself).
wjbr 10/24/2024||
static int pretty_float_equal (float a, float b) { return fabsf(a - b) < FLT_EPSILON; }
varjag 10/24/2024||
This made me immediately think whether MIT Loop of Common Lisp was an inspiration here. Checked the user's profile and sure enough, a lisper!
aartaka 10/24/2024|
Yes, LOOP is a huge inspiration and my favorite programming language!
dfox 10/24/2024||
The code asumes that C17 has C++-style auto (https://github.com/aartaka/pretty.c/blob/master/pretty.h#L11...), it does not (in C auto is storage specifier that is equivalent to no storage specifier).
aartaka 10/24/2024|
C17 doesn't have auto, but C23 does, and thus my `__STDC_VERSION__ > 201710L` (notice the greater than sign, it doesn't include C17 itself.)
dfox 10/24/2024||
Ha, apparently the N2310 working draft is not the last one :)
pipeline_peak 10/24/2024||
For each looks convoluted, you shouldn’t have to list the type.

It should be no harder than C#’s foreach(var i in list)

aartaka 10/24/2024|
Indeed, I might need to revisit foreach with type inference. Should be totally possible.
taylorius 10/24/2024||
Sorry for what is probably a stupid question. Does pretty.c act as a preprocessor or sorts, converting your pretty.c script into actual c, that is then compiled? Or is it a virtual machine that interprets your pretty.c script?
aartaka 10/24/2024||
It's a set of C Preprocessor macros, so you don't even need to somehow process the code—you just #include the header and hack away as if it was regular C!
designed 10/24/2024|||
Preprocessor of sorts. From the readme:

The goals for Pretty C are:

- Provide so much syntactic sugar as to cause any C developer a diabetes-induced heart attack.

- Deprecate Lua, Python, JavaScript, Ruby and a dozen other languages, because Pretty C is the ultimate scripting language, but lightning-fast and strongly typed!

- Including only one header (yes, Pretty C is a header-only library #include-able from arbitrary C file!) to turn any codebase into a beginner friendly one.

wffurr 10/24/2024||
It’s not a preprocessor or compiler. There’s no binary. It’s just a bunch of C code in a header: macros, functions, etc. that you can use to write different looking programs but that are still C.
w4rh4wk5 10/24/2024|
I've seen this implementation of defer a few times now. I really dislike calling this defer (like the keyword in Go) as the given code won't be executed on return.
aartaka 10/24/2024|
Scoping defer to a block is actually more useful and explicit than function-exclusive that Go does, so I consider that a feature.
w4rh4wk5 10/24/2024||
No no, I think you misunderstood my critic. Defer working on block-scope is fine; however, if I exit the block through a return (or break), the deferred function is not called.

To my knowledge, you need a compiler extension to implement this in C (or use a C++ destructor).

More comments...