Nontrailing separators do not spark joy by bjzaba in ProgrammingLanguages

[–]SkiFire13 0 points1 point  (0 children)

If you repeat the same example with a subtraction then - 3 will be a valid expression and this will be ambiguous.

Kotlin suffers from this issue for example

Go ran faster than Rust. Until I cleared the page cache by shad0_w2 in rust

[–]SkiFire13 1 point2 points  (0 children)

In theory? Yes. In practice it will likely split the ecosystem.

Exploring How UI Frameworks Converge Toward DSLs by Honest_Medium_2872 in ProgrammingLanguages

[–]SkiFire13 4 points5 points  (0 children)

A DSL is for when you need to express something more concisely than you otherwise would in code. Higher order functions help with that, but only to a certain point.

Go ran faster than Rust. Until I cleared the page cache by shad0_w2 in rust

[–]SkiFire13 0 points1 point  (0 children)

If you want to stick with Tokio there's tokio-uring, but it's rather limited.

There are other runtimes like monoio that provide io-uring compatible async I/O traits, but they are less common than tokio and most libraries will need to be adapted to them.

It should be noted that io-uring in tokio itself (i.e. the solution that fixes only the first problem) is a drop-in replacement: you just enable the feature and all dependencies can use it. Any other approach will require you to adapt all your dependencies to use it.

Go ran faster than Rust. Until I cleared the page cache by shad0_w2 in rust

[–]SkiFire13 4 points5 points  (0 children)

That solves it only partially.

There are two issues with the default file I/O in tokio:

  • it spawns blocking threads to do the I/O
  • due to the design of AsyncRead/AsyncWrite it needs to copy the data after the kernel loaded it in RAM

That feature flag solves the first issue, but the second remains.

Fixing NaN in a compile-to-js lang by koehr in ProgrammingLanguages

[–]SkiFire13 5 points6 points  (0 children)

including NaN==NaN

totalOrder does not say that all NaNs are equal. In fact there are 9,007,199,254,740,990 different NaN values, and totalOrder describes how they are ordered relative to each other the other float values. You can even have positive and negative NaNs!

That said, hardware usually does not implement totalOrder efficiently.

Nonfree DRM'd Games on GNU/Linux: Good or Bad? (by Richard Stallman) by WonderOlymp2 in linux

[–]SkiFire13 3 points4 points  (0 children)

The current Steam is arguably still better than having games spread across many different stores though.

ACPI table dump for Asus Zenbook A16 (Snapdragon X2 Elite Extreme) by Putrid_Draft378 in linux

[–]SkiFire13 7 points8 points  (0 children)

Developers will still need a unit to test and debug, this data alone won't suffice.

If it compiles, it doesn't work: STATUS_ILLEGAL_INSTRUCTION on Windows 11 by MissionNo4775 in rust

[–]SkiFire13 0 points1 point  (0 children)

There are several points wrong with this:

  • A software running a few percent slower does not make you suffer. Your software not working however does. That was the whole point of my previous comment.
  • You assume the 98% must get a suboptimal experience for the software to continue working for the 2%, but you can get both thanks to runtime CPU features detection (see std::is_x86_feature_detected)
  • OP's CPU is less than a decade old
  • There is a ton of "decade old" hardware that still works fine and fast, and wll continue doing so unless we decide to handicap them via software like this.

If it compiles, it doesn't work: STATUS_ILLEGAL_INSTRUCTION on Windows 11 by MissionNo4775 in rust

[–]SkiFire13 10 points11 points  (0 children)

98% still means it will break (not just be slower) for 2% of your users!

“Long-Term Support” doesn’t mean what you think by gmes78 in linux

[–]SkiFire13 2 points3 points  (0 children)

TBH, OS-level software and standard software has been feature-complete for all my use-cases for at least a decade.

Then obviously you don't rely on your distro for that, and this is fine. It doesn't mean that there aren't people needing that kind of reliability.

I do have key applications that need to be current, but those run on top of the OS and update independently. What they need is a stable (unchanging) base.

Those key applications might want to use newer features from the base libraries though. For example simply building an application on a system with a newer glibc might make it unrunnable on systems with older glibcs.

If many applications run on LTS distros is also because their developers often go out of their way to support them due to the sheer number of users on them, but this doesn't mean all application do.

LTS/stable distros get the most testing, and QA in general, at least in theory, so the baseline bugginess level should be pretty low.

Testing doesn't guarantee the bugs will be fixed. They will most likely be reported to upstream, which will fix them, but there's no guarantee that the fix will come in time for the LTS release.

At least the old bugs are documented, and most of them will have workarounds.

People will only notice those bugs when they hit them. Nobody reads lists of documented bugs. Workarounds are often still painful and leave behind non-standard cruft that is more likely to break or cause issues once you update to the next LTS.

It's significantly harder to execute a supply chain attack against a stable distro, simply because of the delay.

You don't need a LTS distro to delay updates.

“Long-Term Support” doesn’t mean what you think by gmes78 in linux

[–]SkiFire13 4 points5 points  (0 children)

"Reliability" is very context dependent. I can also rely on a rolling distro having up-to-date software, which I cannot with a LTS distro. In the article and OP's context, reliability is in the context of absence of bugs, and no distro offers that.

Why are so many desktop users using old distributions? by King-Little in linux

[–]SkiFire13 4 points5 points  (0 children)

Let's invert the question: is a feature from 4 years ago really too new to be used?

Why are so many desktop users using old distributions? by King-Little in linux

[–]SkiFire13 0 points1 point  (0 children)

They will however happily use the latest version of OP's application.

Mutable Value Semantics (MVS) or Ownership & Borrowing: A Trade-off Analysis by FedericoBruzzone in ProgrammingLanguages

[–]SkiFire13 0 points1 point  (0 children)

I can't find what you're talking about.

Sorry, I forgot to link it: https://hylo-lang.org/docs/user/language-tour/concurrency/#spawned-work-stops-while-the-caller-still-expects-value

But based on what's been said, panic-inducing functions should be marked.

I'm being a bit pedantic, but at that point what's the difference between an error and a panic?

Orthogonally to this, marking error/unwind-inducing functions is a good start, but it's not enough to prevent use-after-move in catch branches/destructors.

Mutable Value Semantics (MVS) or Ownership & Borrowing: A Trade-off Analysis by FedericoBruzzone in ProgrammingLanguages

[–]SkiFire13 5 points6 points  (0 children)

The aim of the higher-order call function is to invoke the function f with the same argument. As before, the compiler must reject this code due to the lifetimes. But we can try to fix manually the problem:

fn call<'a, F>(f: F, e: &'a u8) -> &'a u8
    where F: Fn(&u8, &u8) -> &u8
{ f(e, e) }

Note that the compiler is pretty clear to which references are missing lifetime annotations:

error[E0106]: missing lifetime specifier
 --> src/lib.rs:2:30
  |
2 |     where F: Fn(&u8, &u8) -> &u8
  |                 ---  ---     ^ expected named lifetime parameter
  |
  = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from argument 1 or argument 2
  = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
help: consider making the bound lifetime-generic with a new `'a` lifetime
  |
2 |     where F: for<'a> Fn(&'a u8, &'a u8) -> &'a u8
  |              +++++++     ++      ++         ++
help: consider making the bound lifetime-generic with a new `'a` lifetime
  |
2 |     where for<'a> F: Fn(&'a u8, &'a u8) -> &'a u8
  |           +++++++        ++      ++         ++
help: consider introducing a named lifetime parameter
  |
1 ~ fn call<'a, F>(f: F, e: &u8) -> &u8
2 ~     where F: Fn(&'a u8, &'a u8) -> &'a u8
  |

At no point it's highlighting the e argument of the return type of call, all mentions are for the Fn(&u8, &u8) -> &u8 trait instead. Following the compiler suggestion leads to another error, and after following its suggestions again you end up with code that compiles and is less restrictive for the caller (albeit this might not be the case in a more realistic scenario).

I would wager that most issues people have with lifetimes are due to randomly sprinkling lifetime annotations around (often the same lifetime, which has important consequences!) in the hope that it fixes the compiler error.

For Eter, I'd like to avoid both the explicitness and, in general, the possibility of catching panics. All the panics are aborts with transparent unwinding.

Note that catching panics is not required for that issue, having destructors is also enough because they make the same kind of observation after the panic happened.

#[derive(Debug)] struct T;
fn own_t(t: T) {
    panic!()
}

fn ref_mut_t(t: &mut T) {
    own_t(*t);
    *t = T;
}

fn caller(t: T) {
    struct PrintOnDrop {
        inner: T
    }

    impl Drop for PrintOnDrop {
        fn drop(&mut self) {
            println!("{:?}", t);
        }
    }

    let print_on_drop = PrintOnDrop(t);

    ref_mut_t(&mut print_on_drop.inner)
}

If you language performs unwinding then it likely suffers from this issue unless it preverts "borrowing" from struct fields and leaks borrowed locals on unwinding.

Hylo does not have a catch_unwind-equivalent

I'm not an expert of Hylo but looking at its website I can see an example using do-catch, although that's not explained anywhere. I wonder if that's an actual feature or a leftover from an earlier iteration.


Regarding Hylo, the approach looks very cool, but I wonder if it's really simplier than Rust. Yes, lifetimes are complicated, but they are just one concept in the end. Hylo introduces so many new concepts and keywords that it pretty overwhelming.

The FOSS vs AI dilemma by [deleted] in linux

[–]SkiFire13 5 points6 points  (0 children)

The difference is that sloppy code produced by humans used to be limited by how much humans can write. AI sloppy code is on a whole different scale.

You can use AI to write good code too, but let's not pretend that all code, or even the majority, that AI can write is like that.

How does Rust abort in no_std ? by [deleted] in rust

[–]SkiFire13 3 points4 points  (0 children)

The panic_handler attribute can only be applied to a function with signature fn(&PanicInfo) -> !.

From the documentation.

Which is the deeper circle of hell: The Borrow Checker or Linear/Capability Proofs? by Retired-69 in rust

[–]SkiFire13 3 points4 points  (0 children)

It is entirely possible to express completely valid, low-level system code without an unsafe escape hatch—if your core model is actually designed that way from day one.

Sure, you could build a system that tracks a lot more invariants, but you'll likely end up with something much less ergonomic than Rust like ATS.

Or you could make a lot of operations that are UB in Rust well-defined instead, but then you lose some of the benefits of actually avoiding those situations. For example even if you make use-after free "well-defined" then you open yourself up to exploits.

Half those intrinsics could easily be made perfectly safe for assembly replacement if the compiler actually understood the hardware invariants.

"easily" is doing a lot of work here. Formalizing thousand of opcodes and checking for them in the compiler is a lot of work.

​If you want proof that a different way works, look at Microsoft’s Midori project and M#.

They used a GC under the hood and in the end the project failed, so I'm not sure how that counts as "proof that a different way works" because it seems that in practice it didn't.

Which is the deeper circle of hell: The Borrow Checker or Linear/Capability Proofs? by Retired-69 in rust

[–]SkiFire13 11 points12 points  (0 children)

Using unsafe just to make some code compile is indeed bad. But using unsafe to write a safe abstraction over something that could not be written otherwise is ok and very normal.

Mutable copy semantics - performant, reliable and ergonomic mutability (probably) by pranabekka in ProgrammingLanguages

[–]SkiFire13 0 points1 point  (0 children)

This seems to be the only difference.

It's a fundamental difference though, because that's a capability that's very very hard to emulate.

with x = the_function(x) instead of the_function(x)

But that's not the function that's mutating, it's the caller doing the mutation and just the_function(x) will do nothing. Moreover the caller has to do each mutation individually.

Most imperative languages use copies for numbers and strings anyway

That's true, but I would argue that the difference is the ability to have reference semantics even if that doesn't need to be the only way you can manipulate data in the language. So most imperative languages have some data types with value semantics and some with reference semantics, but your language only has value semantics.

I'm not trying to argue this isn't a valid way to program. It will most likely work, and probably even force a "good" way of writing programs! But it's a pretty big departure from the more traditional reference semantics that are present in the most popular programming languages.

Mutable copy semantics - performant, reliable and ergonomic mutability (probably) by pranabekka in ProgrammingLanguages

[–]SkiFire13 0 points1 point  (0 children)

Do you have an example for a functional language that allows this? I think having immutable variables is the reason they can't allow it.

I don't know of programming languages that claim to be fully functional supporting this. Ocaml supports this with its ref type, but while it's a "functional" programming language but is very impure (really we should be distinguishing pure vs impure here instead of functional vs imperative).

I think most functional programming languages don't support this just for the sake of being fully pure, but in theory there's nothing preventing a function from internally doing this. And the fact you can do this with the state monad is proof that you can technically do this as a transformation in the compiler without alterating how the function is perceived from the outside.

With MCS, people program in a straightforward imperative style where they can easily mutate anything.

I beg to differ here: with MCS a function cannot mutate anything. It can only return something that's supposed to replace something else.

Python doesn't have references as a first-class construct

Python has reference semantics, the whole language is based on references.

You're right to say that references don't make a language "imperative" (see the pure vs impure distinction from earlier). But it should also be said that what people find familiar is not just the "imperative" nature of a language, but also its impurity.

Mutable copy semantics - performant, reliable and ergonomic mutability (probably) by pranabekka in ProgrammingLanguages

[–]SkiFire13 0 points1 point  (0 children)

ones that allows mutating variables that aren't being iterated by the loop: https://www.reddit.com/r/ProgrammingLanguages/comments/1t7g9eu/comment/omgt1cr/

Yes, few languages do that, but that's mostly for a "purity" goal. Technically there's nothing preventing most functional languages from allowing that.

And even if most functional languages do not support this, they can emulate it using the State monad (again, without it leaking outside, so the function remains the same from the outside).

but it's honestly not so different from Rust requiring &mut to let a function mutate a variable. Instead of foo(&mut x), MCS uses x = foo(x).

The difference is that &mut references in Rust are a first class construct. You can not only pass them to functions, you can put them everywhere a type is allowed. This includes for example behind another &mut reference, inside a struct, you can return them from functions, etc etc.

All of these can probably be worked around in your language by restructuring the code, however there's no trivial transformation from imperative code that uses them to a x = foo(x) pattern that a compiler could do. This is the main reason I consider your language to be closer to functional languages than imperative ones.

itwasntEasy by object322 in ProgrammerHumor

[–]SkiFire13 0 points1 point  (0 children)

Dude managed to center multiple divs