That turns them into a reply, sure, but the problem is the same. Once there's a quote post with the same context, the thread usually goes two separate ways making it hard to read through, because there are now two diverging topics. The flatpak v2 systemd thread from a couple days ago is a good example of that I think. There's like three akkoma quote posts in the thread that all keep the same context, including a broken thread thanks to signed fetch.
> Once there's a quote post with the same context, the thread usually goes two separate ways
That happens with threads anyway. It's fine.
As things stand, this is, I believe, an exhaustive list of my options here:
1. Implement quote-posts and then kill myself. 2. Kill all quote-posts by rejecting them at the MRF level. 3. Turn quote-posts into replies. 4. Hack into all servers that implement quote-posting and remove the code that supports this terrible misfeature, thereby killing this problem at the source.
I chose option 3. I didn't like options 1 or 2.
> The flatpak v2 systemd thread from a couple days ago is a good example of that I think.
hachi derm dot input/output (one of the Individual Eleven) refuses to allow FSE to fetch posts from them.
> There's like three akkoma quote posts in the thread that all keep the same context, including a broken thread thanks to signed fetch.
That's Akkoma's problem.
> Yet you really like Ruby. The two aren't that dissimilar.
Well, I haven't ever downloaded a release of Ruby and found a .DS_Store file in it. Programs I wrote for Ruby 1.6 still work. I have never had to update a separate VM in lockstep with updating Ruby.
"When you mix water and brandy, you ruin two good things." --a guy that thought brandy is good but in any case he is correct about the *principle*
Elixir is like if you took some of the parts of Ruby and shat them into Erlang and then in places where Ruby was nice, you decided to replace it with whatever Python does. As shitty as Elixir is, it's nowhere near as terrible as Phoenix, which attempts to replace sensible Erlang CSP style with shitty Rails shit that sucks and the unrepentant PHP dipshit DHH only made Rails because he couldn't figure out what to click on to see the Logo Turtle when he got a Mac:germanB:ook :germanB:ro. Every time I trace a bug to a five-clause `when` in the Pleroma source code, I get the urge to figure out who is in charge of Elixir and drive to his house.
Until you have to debug and it matters which of the clauses failed.
> Old man
I hated Rails when I was an age that people called me young and stupid and now I say Rails sucks and people say I am old. I hope that eventually people will engage with the things I say instead of guessing which reason *besides* the stated one is the reason I say the things I say.
Nothing of value, like usual. Just a bunch of smug posts from corpo plants in the linux space.
>That's Akkoma's problem.
Including an FSE problem now. But no hard feeling of course, everybody has their own preferences. I'm not a fan of quote posts being used like on twitter either, the "lmao, look at this idiot" trope.
>five-clause `when` in the Pleroma source code, I get the urge to figure out who is in charge of Elixir and drive to his house.
@p@j@Junes@phnt@SilverDeth@ElDeadKennedy@ceo_of_monoeye_dating when thing I don't might too much although I'm not a huge fan of function overloading, but it reminds me that I really dislike the lack of early return in Elixir, it makes for such horrible code you'd think if was made for forcing hadoken-indentation.
In reality you should wrap it all in a tuple for error handling which makes the issue clear, but it is true that it isn't the case in most code people write, including Pleroma
with {_, true} <- {:check_one, check(var)}, {_, true} <- {:check_two, check2(var)} do process(var) else {:check_one, e} -> {:error, :check_one, e} {:check_two, e} -> {:error, :check_two, e} end >rails
I think it is fair to say that Ruby is hot garbage. Especially if you need something that doesn't perform awful.
What people *should* do and what they did do are often different; what the language encourages, etc. But I didn't write this codebase. Here it is and I have to deal with it.
>> rails > I think it is fair to say that Ruby
Ruby isn't Rails. Ruby is nice. Try using the language without touching Rails. It's enjoyable. You fire up irb, load up a JSON parser, walk down a data structure, toss a few transformations at it. It's nicer than ever touching jq.
> Especially if you need something that doesn't perform awful.
void *do_something(thing **p) { if(!p) return nil; /* We have just added an invariant. We can proceed down the line, gradually narrowing the number of cases we need to deal with instead of branching. You can sort of do this in Elixir but it requires you to name all of the intermediate steps. This makes control flow difficult to read. You look at a big Scheme codebase and they end up simulating this construct with call/cc or something. You can't do that in Elixir, though. */ ... return p; }
Yeah, that can be very unintuitive at first. Same with implicit return when you try to log a result of a call the function retuns: def func(var) when is_integer(var) do var = var + 5 ret = process_var(var) Logger.debug("#{__MODULE__} func/1 returning: #{inspect(ret)}") ret end It's very easy to return the Logger output and then wonder why it doesn't work, especially for someone new that writes something like this: var = if asdf == true do var = process_var(var) Logger.debug("#{inspect(var)}") else nil
@phnt@j@Junes@SilverDeth@p@ElDeadKennedy@ceo_of_monoeye_dating Sadly what might be unintuitive at first becomes "God dammit" when you go back from months of not using Elixir (and using basically any other language), plus also really ugly diffs and merge conflicts.
@p@CrayonEater@ElDeadKennedy@Junes@SilverDeth@ceo_of_monoeye_dating@j Or alternatively you can abuse the fact that a failed with clause returns the latest attempted match and generate the error directly in the function called. I don't think I've seen someone do that thankfully, since it is ugly.
@lanodan@j@Junes@SilverDeth@p@ElDeadKennedy@ceo_of_monoeye_dating In general I find Elixir requiring quite a big mindset shift from different languages. Certainly for me coming from C and C++. Similar to what reading Erlang at first was to me. An almost alien language.
After a while it sort of clicks all in place and you end up liking it, or it doesn't click and at that point it's probably not a language for you.
def gcd x, y if x < 0 || y < 0; raise ArgumentError, "gcd(#{x},#{y})"; end if x == y; return x; end if y == 0; return x; end if y > x; return gcd(y, x); end gcd(x-y, y) end
It's that you can increase invariants as you progress down. You know x-y>=0 because x>y and both are positive by the time you get to the subtraction. You just eliminate case after case as you progress. You replicate that with single-return and you get nesting. (The example is deliberately contrived; it is just that it gives you easy list of conditions.)
But well knowing myself it'll probably land into a case where C can be too costy like say writing a bootloader, or wanting an early/trusting-trust assembler/disassembler. (I know you gave the assembler example for forth once, and seen it quite few times elsewhere as well)
@p@j@Junes@SilverDeth@lanodan@ElDeadKennedy@ceo_of_monoeye_dating >We have just added an invariant. We can proceed down the line, gradually narrowing the number of cases we need to deal with instead of branching. You can sort of do this in Elixir but it requires you to name all of the intermediate steps.
You do this with a cond clause, no need to name anything. Same thing, slightly different syntax.
cond do is_nil(var) -> do_nil(var) is_binary(var) -> do_binary(var) var == 5 -> do_five(var) true -> do_default(var) end You don't have to call a function after the match either, you can do the processing right after the match. Put this at the bottom of a function and you have the exact same thing.
> You do this with a cond clause, no need to name anything.
You can have giant `if`s but you shouldn't, but you named things in your example. The optimal way to do this, the way Elixir wants you to do it, is to factor it out and use a conditional and unless both clauses are trivial, you make another function; likely, you skip the conditional and you lean on pattern-matching. So you have named the branches `do_binary` and `do_asdf` but you actually do see a lot of things like that in the only Elixir codebase I'm familiar with, and once a dude has three levels of maybe_real_do_uncached_thing (or maybe_drop_authentication_if_oauth_check_ignored or fetch_and_contain_remote_object_from_id or whatever) like, not only is that kind of Railsy but the names are impossible to decipher: they are usually named to fit the conditional so the names make no sense outside the conditional and a lot of them are forced names for intermediate values polluting the module's namespace. It's possible to think this is a reasonable wart, I'm sure, but it's really ugly to me, and I get pretty irritated when the answer to "Why the fuck is this happening to my server?" is delayed because I'm sifting through that shit. (That is to say nothing of the finer points of the differences between `cond`, `when`, `where`, `if`, `case`, etc., and the fact that a nominally *functional* language should have sensible operator precedence.)
> You don't have to call a function after the match either, you can do the processing right after the match.
I'm not sure what you mean to convey. I do know how you do those things in this language; I just don't like that the flow of execution is on rails.
> I think that's a fair replacement for early return.
I mean, I think it's a mess. Especially in a lived-in codebase, you get buckets of this stuff and it's a pain to trace execution.
> a descriptive function name
Well, you can name an accumulator "a" or "acc" or "i" or "n" and it's a name that stays out of your way, but you can't do this at the module level. You're taking something that would have, as its only scope, the ten-line function it lives in, and then you're putting it into the module's scope: this means you've got to name it a little more carefully because it's now part of a module instead of a local variable. It's not always obvious where it's called from, especially in a big module, and this gets in the way of debugging. Having to come up with a module-scope name for an intermediate calculation gets in the way of sketching, so people don't do it, and sometimes it's not even possible: this is an intermediate value with no meaning outside the very narrow scope, and what *is* it? Sometimes it's nothing. It's half an equation.
Look at the quadratic formula, right, you've got a +/- and to avoid calling sqrt twice, people store the intermediate value and then return a tuple (however tuples are done it the language you're using) with (-b-val, -b+val). What's a good name for that value? Or you calculate stddev and you've got to have an accumulator for the sum of the squares of the differences of the individual elements and the average, and if you don't have a `reduce` builtin, you put it into a variable and you general call it something short enough to be legible, `ssd` for `sum of squares of differences` or something, fine for local scope. Say you're doing it in awk:
:mycomputer: total=0; for(i in a) total += a[i]; avg = total/length(a) :mycomputer: ssd=0; for(i in a) ssd += (a[i]-avg)*(a[i]-avg) :mycomputer: stddev = sqrt(ssd/length(a))
I mean, some stuff just doesn't have a good name, but if it's module scope, you don't have the option of giving it a *bad* name.
(You write enough Forth and you see what people mean about not having to name *anything* that you don't want to name.)
> It tends to remove the 20+ line if/switch statements you get in something like C when you need to do this
That is the point of the early return: you shouldn't nest like that in any language, C included.
> It is terrible design to process multiple types of something in a single function.
The World Wide Web does not give you a lot of options: you get to process multiple types of something in a single function. (Worst part about Go is that it wants JSON to be a static data structure and JSON is not a static data structure.)
Pleroma has to do a lot of this due to the having to handle "is this an array or not" or "did the DB return a valid UTF-8 string or just a bucket of line noise because the web is dirty or maybe it's nil, plus if it's a string like we're hoping, what does it look like and which way should we handle it?" (You will often have a bad time if you see `conn |>` in the function and you will almost always see that and it's the context trash bucket, it's like inside-out manual AOP, and AOP is already bad enough. "I've never looked in this module", I think. "`conn |>` could be here. `conn |>` could be anywhere." Illicit stimulants coursed through my veins as some square wave remix of something that used to be music reverberated through the cheap-ass Logitech subwoofer. "With a TCP socket, you can send data anywhere you want," I say to myself, out loud.)
> You would name that function the same in other languages
I wouldn't name *anything* that in *any* language. If you have an entire sentence with conditionals as your function name, then you don't have a function and I don't know what you *do* have but I don't want to touch it. You see names like that in Rails or in Java. (Most of the code in the world is bad so you have to cope, and if you're in a codebase that looks like that, you wanna fit the style so that you don't clash at the interface level, but you don't want to forget that it's bad.)
> If it really needs to exist, multiple functions guarded with a when are slightly better at least in my mind.
A `when` is like a really long chain of `&&`s. You don't want to if you can avoid it, and you definitely don't want to put any side-effects in there, but people do that.
> It's a different code style basically. Same endless arguments about different code styles being better than others apply here as well.
It's a different set of tradeoffs. You don't get a language with no warts; coding style dictates which warts you'll put up with. I really like using awk but there are some infelicities; same with anything, but awk is nice to use when what you have fits awk's way of doing things.
> Just that you can also do this, nothing more:
Yeah, I get that part, that's why I was confused about why you were saying it.
>The optimal way to do this, the way Elixir wants you to do it, is to factor it out and use a conditional and unless both clauses are trivial, you make another function; likely, you skip the conditional and you lean on pattern-matching...
I think that's a fair replacement for early return. If you do it correctly the complexities get resolved elsewhere and all that is left is a descriptive function name and you shouldn't need to care about how it is done. It does have an OOP abstraction smell to it though. That's the point of the `maybe`s, sometimes coupled with the `do`s; do this for me if it should be done, ignore when it shouldn't, I don't care about the details. It tends to remove the 20+ line if/switch statements you get in something like C when you need to do this and avoids dealing with fallthroughs in switch-case. And removes some noise from the function making it more readable/understandable. Of course, there are times where the 20 line case/cond is also more appropriate in Elixir.
>So you have named the branches `do_binary` and `do_asdf` but you actually do see a lot of things like that in the only Elixir codebase I'm familiar with
Kinda, you don't have to do that when the handling is simple like extracting something from a map or a list in a few lines. Arguably if it is more than a couple (10-15+) lines, you should make a function for it anyway. The example I gave above isn't really ideal since I would try hard to not write it. It is terrible design to process multiple types of something in a single function. If it really needs to exist, multiple functions guarded with a when are slightly better at least in my mind.
>fetch_and_contain_remote_object_from_id
You would name that function the same in other languages, because it's basically an entrypoint that does it all, an internal pseudo API. Unless you wouldn't write it all and wrote the same handling multiple times which is worse. Or caught the libc string manipulation function naming disease and called it robjfc_id or whatever. Hello vsnprintf and suckless function names that mean almost nothing at a glance.
>It's possible to think this is a reasonable wart, I'm sure, but it's really ugly to me,
It's a different code style basically. Same endless arguments about different code styles being better than others apply here as well.
>I'm not sure what you mean to convey. I do know how you do those things in this language;
Just that you can also do this, nothing more: # Bleh, multiple types. cond do is_map(var) -> var["some_property"]
@p@j@Junes@SilverDeth@lanodan@ElDeadKennedy@ceo_of_monoeye_dating Alright time for a necro after I had a "fun" time with C's terrible early-return style error handling and the whole reason why I mentioned Elixir's `with` as a way better way of handling errors/cases.
Case in point attached code I tried to write as elegantly as I can write C. All it is supposed to do is (un)set a window property and create it when it doesn't exist, because mpv is dumb and still renders video to an unmapped window and iconified window. There are 3 goto's to cleanup code that are equivalent to just copy-pasting the same early return statements three times, and a single early return when the first function call fails.
It handles an edge case (property creation), errors and the actual purpose of the function in a single top-down fashion normal for imperative languages. The purpose of the function is stuck completely at the bottom, the "what it needs" and "what it does" is completely split by "but what if this happens instead" code; the whole paradigm of reducing cases until a final desired one at the bottom didn't serve this function well at all. It made it harder to grasp, understand and frankly ugly.
This is the whole point of why I prefer sticking all the checks for the desired purpose at the top, which Elixir's `with` is perfect for, writing the actual purpose of the function right below that, and the whole "but what if this happens instead" goes at the bottom after the actual purpose without interrupting the main purpose of the function at all. You have "what it needs", "what it does" and only after that the "but what if this happens instead".
PS: Too lazy to loop the XGetWindowProperty call to get all properties without dropping anything. image.png
@phnt@j@Junes@SilverDeth@p@ElDeadKennedy@ceo_of_monoeye_dating Note that the goto stuff isn't exactly modern, like Go introduced defer ages ago and it got adopted by the vast majority of C-like languages, as well as Perl (which already allowed for or die "error message" that you can slap after functions which prints the error and then exits).
Also gotta love the monstrosity of libX11, like it's usual thing of having dozen of parameters per function, making this code ugly by default.
btw if(data) free(data); is useless here, but also useless in general as free() is a no-op when given NULL. Plus given you're not getting more than 64 items, might make sense to avoid the malloc and just use the stack.
@lanodan@j@Junes@SilverDeth@p@ElDeadKennedy@ceo_of_monoeye_dating >like Go introduced defer ages ago and it got adopted by the vast majority of C-like languages Yeah, patiently waiting for C2X to introduce it and wait a decade before it slowly proliferates to projects and not get refused in MRs. You can use __attribute__(cleanup) in gcc and clang, but it isn't as nice.
>Also gotta love the monstrosity of libX11, like it's usual thing of having dozen of parameters per function, making this code ugly by default. Structs considered harmful.
XFree accepting a NULL pointer isn't officially in the X11R7.7 spec, but is in the man page for Debian, so it probably is not portable across platforms. Amazing. Not that it matters since the server should return a valid address on Success no matter what.
And I think only bit of X11 that got a specification is the protocol, plus everyone switched to X.Org when it comes to X11, the days of stuff like Xsun and Xsgi are long gone.
Reminds me of why I stick to foss for personal projects so bug reports and patches can be sent. (And job I got is UNIX conformance so stuff like that would be their bug, mwahaha :D)
> wouldn't surprise me to see it break in some weird systems
Well, now that I think about it, like, I can't come up with a reason for that to ever fail. You've got your list of allocations, you've got to look it up to free it. Apparently glibc (:rms:) and musl (:cereal:) and Plan 9 libc (:kenbw:) all handle free(0); I didn't check the other two but it's just an explicit check in Plan 9.
@lanodan@ElDeadKennedy@Junes@SilverDeth@ceo_of_monoeye_dating@j@phnt Well, I didn't have one for Felker but I figured :cereal: would work. (The Plan 9 libc, you know, people say "but no Google Chrome and no libreadline" and ignore the "Ken wrote this OS" part; it kind of irks me.)
A lot of embedded stuff fails on a lot of weird cases and usually the answer is "Don't do stupid shit, X3J11 can't help you here. Patch it out or don't." That's a reasonable answer where you're counting bytes. But I'm sure I've seen it somewhere besides embedded libraries.
> But even Microsoft implemented that correctly, which is unusual for them
Well, "to spec" rather than "correctly". Edge-case behavior is "What should you have a reason to expect?" rather than "What's the best thing to do in this case?" A *good* spec is more descriptive than prescriptive, but "Well, that's not defined, what if someone digs up a one's complement machine and then tries to port your code to it? Huh? HUH?!" The reason integer overflow isn't defined is to give implementers space to do things: you can't have guards around every increment so while there were one's complement machines, you couldn't rely on that behavior. gcc's current behavior has the tail wagging the dog: "You can't possibly know that `for(int i=1;i;i++);` will ever terminate!" is pants-on-head retarded.
Rant aside, I mean, you generally don't want to rely on Microsoft to do what every sane implementer does.
And like libreadline I can quite understand it, I'd quite want the readline features in ⑨ but no way I'd port GNU code to ⑨, plus would also rather have it in a more plan9 way. Like could try stuff like separating input and output buffer like in sam.
@lanodan@ElDeadKennedy@Junes@SilverDeth@ceo_of_monoeye_dating@j@phnt I mean, yeah, I'll take a clean OS that Ken wrote that is "missing" some stuff but has stuff no one else has managed to build. The readline stuff doesn't really bother me. The whole interface acts pretty consistent and even if moving around lines was slower (I don't think it is), it's still not what slows me down, that's "Oh, dear god, what is this thing even doing?" and I don't have that problem nearly as often.
@p@j@Junes@phnt@SilverDeth@ElDeadKennedy@ceo_of_monoeye_dating it's something I kind of miss here, mostly because I hate repeating myself, specially when it's commands, plus mouse chording doesn't really works that well for me. Although IIRC there's a way to replicate fc(1) but it's more a hack than anything else.
Also a takeaway for me in Plan9 and 9Front isn't only the code but more the community centered on it's own interests rather than corporate or strawman-popularity ones. It makes the codebase so much cleaner, but also way more resilient as there's little to no outside dependencies (importing anything too-big-to-fork is something I so wish Unix communities would avoid like the plague).
@lanodan@ElDeadKennedy@Junes@SilverDeth@ceo_of_monoeye_dating@j@phnt That's the ideal part, right, like, "Here, we're doing this thing and it will never make money or appeal to consumers, we're just making something that is supposed to be good." First goddamn OS I ever used that wasn't talking down to me.