• 0 Posts
  • 51 Comments
Joined 2 years ago
cake
Cake day: July 31st, 2023

help-circle
rss
  • I actually jumped ship a while back. I agree that Plex is a business and they do deserve to get paid for development and infrastructure costs, but it’s the blatant enshitification that I have a big issue with.

    They chose to lock a previously-free feature behind a paywall for everybody and asked for even more money to get it back. The less shitty alternative would have been to ask only the users who needed to use the relays to purchase a Plex Pass. Or, if they wanted to make it seem like a positive thing, they could have made the new subscription into an “enhanced quality” remote streaming experience that enabled higher bitrates over relays.

    They gave their users the middle finger by picking the most transparently greedy option that they could get away with justifying.





  • Software costs money how would they continue to developed it if not getting paid?

    Apparently a hot take as evidenced the downvotes on my other comments here, but by adding things people want instead of taking away things people already have and charging more for it.

    They don’t even have the excuse that they need to pay for the bandwidth costs of relaying video from servers to clients. Video is streamed directly from the user’s self-hosted server, using UPnP or NAT-PMP to make the server accessible from outside the local network.


  • And this isn’t a new feature they’re adding. Remote streaming was already implemented and generally available to users.

    I don’t discount there being a cost in maintaining code over time, but it’s not as though they have to spend any significant employee time on improving it. They already support UPnP and NAT-PMP to have the clients connect directly to the self-hosted servers.

    It would be nice if they added NAT hole punching on top of that, but it’s evidently good enough to work as-is in its current form. If they’re not even running relays to support more tricky networks (which the linked support article has no mention of), keeping this feature free costs them literally nothing extra.


  • No, it’s still wrong.

    We have ways to do NAT traversal and hole punching on consumer routers. Failing that, UPnP and port forwarding exist. Or, god forbid, IPv6.

    In the rare case that literally none of those are an option, they would have to use TURN to relay between an intermediary. That is a reasonable case to ask the user to pay for their bandwidth usage, but they don’t have to be greedy fuckers by making everyone pay for it.

    This is enshittification and corporate greed. Nothing more, nothing less.







  • That’s not the point, though. The point is to use a nominal type that asserts an invariant and make it impossible to create an instance of said type which violates the invariant.

    Both validation functions and refinement types put the onus on the caller to ensure they’re not passing invalid data around, but only refinement types can guarantee it. Humans are fallible, and it’s easy to accidentally forget to put a check_if_valid() function somewhere or assume that some function earlier in the call stack did it for you.

    With smart constructors and refinement types, the developer literally can’t pass an unvalidated type downstream by accident.


  • You’re going to need to cite that.

    I’m not familiar with C23 or many of the compiler-specific extensions, but in all the previous versions I worked with, there is no type visibility other than “fully exposed” or opaque and dangerous (void*).

    You could try wrapping your Foo in

    typedef struct {
        Foo validated
    } ValidFoo;
    

    But nothing stops someone from being an idiot about it and constructing it by hand:

    ValidFoo trustMeBro;
    trustMeBro.validated = someFoo;
    otherFunction(trustMeBro);
    

    Or even just casting it.

    Foo* someFoo;
    otherFunction((ValidFoo*) someFoo);
    

  • If it were poorly designed and used exceptions, yes. The correct way to design smart constructors is to not actually use a constructor directly but instead use a static method that forces the caller to handle both cases (or explicitly ignore the failure case). The static method would have a return type that either indicates “success and here’s the refined type” or “error and this is why.”

    In Rust terminology, that would be a Result.

    For Go, it would be (*RefinedType, error) (where dereferencing the first value without checking it would be at your own peril).

    C++ would look similar to Rust, but it doesn’t come as part of the standard library last I checked.

    C doesn’t have the language-level features to be able to do this. You can’t make a refined type that’s accessible as a type while also making it impossible to construct arbitrarily.


  • Unless you’re a functional programming purist or coming from a systems programming background, it takes a lot longer than a few days to get used to the borrow checker. If you’re coming as someone who most often uses garbage-collected languages, it’s even worse.

    The problem isn’t so much understanding what the compiler is bitching about, as it is understanding why the paradigm you used isn’t safe and learning how to structure your code differently. That part takes the longest and only really starts to become easier when you learn to stop fighting the language.




  • The first directory block is a hole. But type == DIRENT, so no error is reported. After that, we get a directory block without ‘.’ and ‘…’ but with a valid dentry. This may cause some code that relies on dot or dotdot (such as make_indexed_dir()) to crash

    The problem isn’t that the block is a hole. It’s that the downstream function expects the directory block to contain . and .., and it gets given one without because of incorrect error handling.

    You can encode the invariant of “has dot and dot dot” using a refinement type and smart constructor. The refined type would be a directory block with a guarantee it meets that invariant, and an instance of it could only be created through a function that validates the invariant. If the invariant is met, you get the refined type. If it isn’t, you only get an error.

    This doesn’t work in C, but in languages with stricter type systems, refinement types are a huge advantage.