Hacker Newsnew | past | comments | ask | show | jobs | submit | Panzerschrek's commentslogin

> reunite the USSR of which Poland was a member state

It wasn't. Poland was formally independent country, but practically under control of Moscow.


No, thanks, I require no modernization. I just need stable, but customizable UI, so that I can customize it once and use it my entire life without needing to adopt to new stuff constantly.

I also need an easy backup strategy for it. I want a single file blob somewhere I can throw in Syncthing l, and have it get backed up to the cloud.

configuration.nix is that file for me. I gave up entirely on desktops and panels, and instead use xmonad and dmenu. I've had to adapt to exactly zero change in my ux in ten years. For me, enrichment comes from studying and experimenting, not from adapting every month or two to things that make somebody else feel good about changing.

> stable, but customizable UI,

Which you can't get without modernization because pre-modern solutions don't offer that. But also, what do you do throughout your "entire life" if you want to customize something to do what's not implemented? That's also modernization.

Effectively all you're asking for is a simple opt in so you can change at your own pace


> Which you can't get without modernization

What kind of "modernisation" do you think is necessary for that? XFCE like 12 years ago was already pretty customizable and it remains customizable. Themes, colors, button layouts, some behavior and effects can be changed. XFCE panel allows full customization - I can choose its position, start button logo and text, shortcuts and other elements. It's as flexible as it can be.


XFCE is GTK-based, which is infamous for breaking plugins on some version updates! That includes your panels with "full customization". So you don't have stability, that would require modernization of development practices.

> It's as flexible as it can be

Is it as flexible as allowing users to enable users the OS-wide app interaction ideas described in the blog/video presentation?


In some cases I use binary fixed-point numbers. In certain aspects they are much better than floats - no precision loss happens in addition/subtraction (if no overflow/underflow takes place), additions and subtractions are typically faster (since it's just an integer operation internally), casting from and to integers is also cheap (requires only bit-shift).

Multiplications are a little bit tricky. Multiplication by an integer is trivial. Multiplication of two fixed point numbers produces the result with the number of fractional binary digits equal to sum of the number of fractional digits in source numbers. The result may be stored in an extended type, truncated down or rounded.

Divisions work fine too, but sometimes may be slower compared to float types, because CPUs can for some reason do much faster floating-point divisions compared to integer divisions.

The only disadvantage of fixed-point numbers is that it's required to keep a balance between range and precision carefully. One can't just use some specific precision in the entire codebase, typically precision should be selected for each individual operation.


A neat trick many people aren't aware of is that you can treat binary floats as saturating fixed point, subject to some qualifications (generally the next larger float type can represent any given fixed). Float operations internally are "just" fixed point ops with some normalization steps and rounding bits on each side, so if we use a float type with enough mantissa bits to hold the fixed point value all we have to do is mask off the extra precision to get back to fixed point. This similarity to fixed point is exploited in some modern NPU hardware by storing only one exponent for an entire block of floats, with a wide fixed point unit doing the actual work, a.k.a block floating point.

This hack has some interesting advantages. Float to integer is still only a few cycles, the masking is one line of libm functions, you get better (and dynamically selectable!) precision, it has gradual underflow and overflow, you can write numeric code like usual, and normalization is automatic.


Worth noting the gap between floating point vs integer division isn't that bad on newer CPUs these days. On Zen5, for instance, DIVSD has a latency of 13 cycles vs 16 cycles for DIV.

>Divisions work fine too, but sometimes may be slower compared to float types, because CPUs can for some reason do much faster floating-point divisions compared to integer divisions.

The mantissa of a floating point number has less bits than the integer type of the same byte size.


Fixed-point can also be much more efficient in terms of bits if you know you are staying within some range. If you're working with 32 bits this can be a pretty big difference (4 billion values vs 8 million for single-precision floats).

> Rust: Drop runs automatically at scope end, so this specific bug simply doesn’t exist.

That's why having no auto-destructors is a dead-end. This is the greatest mistake of such languages like Zig or Odin.


They don't play nicely with arena allocators. And arenas is what you reach for if you have clear lifetime bounds: e.g. a single request with arena never de-allocates individual objects, nukes arena when done. That gives you an easy verifiable protection against leaks, data (and cache) locality and deallocation that cost zero cpu cycles.

Rust supports arena allocator https://docs.rs/bumpalo/latest/bumpalo/ and the dropping plays well with arena as long as you use bumpalo::boxed::Box

It's technically possible to perform arena-based allocation and still have compiler checks. The compiler just need to track objects allocated with an allocator and prevent destructing the allocator itself as long as there is at least one object using it.

It's like view span objects in rust. The compiler knowns that a span is logically connected to the parent object and don't allow destroying it when such span exists.


Why not though?

Have a boxed object have implemented drop, then when the box leaves some scope the Box will clean up it's stuff (drop implementation if there is any) and deallocate it's memory using the allocator (which the arena will treat as noop).


Yes, that would work. But you would need to carry pointer to allocator inside box and it is extra code to run for every object.

extra code meaning the drop implementation or something else?

Yes, I mean the code in the drop implementation.

it isn't really overhead, assuming you don't forget defer ... accidentally

You can statically analyze for leaks.

But with static analysis it's still possible to miss some leaks or to have false-positives. That's why an integrated language mechanism preventing such leaks is much better.

Yes, so you pick "false positives" instead of "missing some leaks" and you build a way to mark code as "unsafe".

This is not fucking rocket science

> That's why an integrated language mechanism preventing such leaks is much better.

No categorical difference, except one is opt-in. You can even design your static analyzer so it analyses the code of dependencies that haven't opted in.


Making things opt-in means that it will happen less often, making them opt-out that they will happen more often. In this case, on the one hand you have destructors that don't run when they should, and on the other you have destructors running at a more granular level than you'd want sometimes. I know which human failure mode I prefer.

In practice as long as you stick to using zig std, an analyzer can get quite far

I doubt that. I mean I'm sure it can catch some cases, but if it worked reliably it would just be a language feature.

Maybe the authors just don't want to do it?

Anyways: its possible, I am building it as a very side project.

github.com/ityonemo/clr


Pretty strong censorship. That's why I can't even open this link in my country.

TCP isn't really necessary. Usually a language server just uses stdin/stdout, which are just pipes.

And overall overhead for running a language server in a separate process isn't that big. LSP is designed in such a way that only minimal amount of information is needed to be passed, like edits or short responses. Packing/unpacking JSONs isn't a bottleneck, the heaviest job like program analysis is done in the language server itself without interprocess communication overhead involved.


A lot of things described in this article applicable not only for Rust, but for almost any language. Like it's obvious that requests should be handled asynchronously and that conversions from/to UTF-16 are needed. But it's actually not so hard.

I have written a language server for my language too. The hardest thing was to find a way allowing providing useful autocompletion for a document in edited state, when it's not syntactically-correct. This is the trickiest part how to deal with such incorrectness without missing all the context necessary.


I've not written a language server but have written a language plugin for IntelliJ.

I started with writing a correct recursive descent parser. I then extended it to detect, report, and recover from common syntax errors as I encountered them so that the parser is robust. And adding a parser test case for each of these (e.g. one test for each branch through an EBNF construction).

Some examples are:

1. missing keywords when the keyword can be detected from the current context (e.g. missing semicolon at the end of a statement);

2. using the wrong token (e.g. `:` instead of `::` in a C++ namespace qualified name);

3. detecting and ignoring whitespace in a whitespace-sensitive qualification (e.g. in XML QNames);

4. keeping in the prolog state (where functions are defined) when there are errors so that functions after the error don't get lost;

5. lexing incomplete literals like `10e` so they can be handled as integers in the parser and emitting an error for them.


> recover from common syntax errors

It's a dead-end. Sure, it can work in simple cases, but there will be always a case where such syntax recovery isn't possible. That's why relying only on syntax recovery isn't an option.

Because of that I use a different approach. I do parse on each document editing, but such parsing is guaranteed to produce valid results only up to the point with broken syntax, where editing usually takes place. Such parsing is enough to reconstruct location of the point where editing takes place (namespace/class/function) and to reconstruct local context (local variables declared prior to editing place). This allows to perform almost perfect autocompletion by suggesting global and local names available at the editing point. In order to provide proper suggestion of non-local names declared after the editing point, I do keep a structure for the most recent document state with valid syntax.

With features like "go to definition" I do the same. I store a hash-table with location to definition point mapping, but it's updated only from time to time and only if document syntax is valid. In order to be usable for cases with edits made after building such hash-table I just perform text-based position mapping using accumulated edit events.


> the simplest infinite loop

An infinite loop which does nothing is practically useless. So, compilers optimize it out. That's the whole philosophy of modern compilers - to reduce execution time by preserving semantics. In case of an infinite loop elimination it's an optimization making code infinite times faster.


But also very different. If code below this loop executes after elimination and wouldnt have before, that is a very significant change in semantics

I don't see how it can be useful. It's almost always an error to write such a loop. The only reason for it to exist is in very low-level code to do nothing, but for such cases using something like an external function written in assembly is perfectly fine, no C++ standard changes are necessary. It's even makes things harder by complicating the standard with little to no benefits in exchange.

On the other hand the idea that repeated iteration warrants a carve out is in itself curious.

I'm sure there will be some bullshit example of how after inlining you can find repetition like this but clearly other languages get along fine without prohibiting infinite loops.

Furthermore, if the goal was to allow for code motion between identical loops absent side effects they could have just said that and spared the ordinary infinite loop.

In a world where C++ is a language unrelated to C another reasonable position would have been to prohibit spelling loops that cannot terminate and provide a fix it for the possible meanings (unreachable, spin).

Injecting a side effect to solve this issue is just horrendous


You can write very low-level code without mucking around with assembly. For example, it's obvious that ARM's cortex-M cores were designed to be possible to code for entirely in C. For example, the interrupt mechanism follow the platform's calling convention so an interrupt vector can just be a plain C function. And something being usually an error doesn't make it a good idea to be undefined, nor does it explain the behavior that they have defined.

It is certainly not almost always an error.

It is a very normal thing to do when doing embedded programming. It normally means "I do not know how to handle this error. Let the watchdog reset me."


Am I right that Voodoo had no early depth test and thus triangles are textured even if they are completely obstructed?

The only PC desktop accelerator that could be considered early depth testing would be tile based 1996 PowerVR. ATI introduced HyperZ in 2000 Radeon DDR (R100), Nvidia followed in 2001 with GeForce 3 LMA. From what I remember Radeon compressed Z gains were surprisingly minimal, like 10-20% when turning it on/off, but Im sure it became crucial later on when real shaders (2.0) started being used.

Amusingly, open source devs are still hacking/improving HyperZ support in 2026. https://www.phoronix.com/news/ATI-R300-Occlusion-Query-Fix

The idea that we should ever allow companies to gate how we use hardware is a joke. The sustainability here is just wildly off the charts impressive. We as a species have blanket legal right to fix things we buy. Any legal monkeying that that from is us an affront to all of us, and if applicable whatever god or gods that made us.


There's no good reason to get emotionally attached to obsolete hardware unless you are running a museum or just doing something cool. AMD doesn't hack on a Radeon R100 for the same reason everyone else doesn't: it's obsolete. It makes more sense to get a modern bargain-bin $30 fanless graphics card than to make your R100 work in any modern computer.

I agree that this is more silly than practical or useful. It's just that old. It's ridiculous.

Reciprocally though 10 even 15 year old GPUs (half as old) are actually often pretty good and useful, for a lot of people. And they're what some people have. Many have multiple 100's of GB/s of memory bandwidth, which is pretty potent even today! That they can be so much better and that they do keep getting better is pretty amazing.

The biggest downside imo is power efficiency. A lot of these cards are on older hotter processes. They lack modern instructions and have to brute force various features. Some signficiant downclocking, undervolting can help a lot for this!

Consumer GPUs have really slowed in progress a lot, especially since 3xxx. With the pressure to upgrade diminishing significantly, I feel like there's an increasing need to have good long term support and care. I don't see anyone but open source as willing able or interested in putting in long term care for products in general. And the broader hope of open source: those willing able and eager to keep making sure the street can find its own use for things. That we can go as far as we might dream.


If you draw front to back that could handle some of that - it does have a Z buffer.

Without an early Z test that would still do all the same calculations only to discard the fragment when it would be written to the frame buffer.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: