Functional programming rah! OOP nah! Or how to know you're a zealot


  • Considered Harmful

    @Arantor said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Dragoon I’m sure something exists in Java that is not awful. I’m not I have personally seen it but I’m sure it exists…

    I quite like JOSM. Its Java warts show here and there like in overzealous DNS caching or not looking very native-GUI-ish but overall it's pretty cool.



  • @Arantor said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Bulb I wasn’t convinced that ‘x is a bad tool because it is written in language y’ is inherently a misfeature of language y… it absolutely can be but my experience is that it’s far more likely that the problem was choosing tool x for a problem that x wasn’t inherently a good fit for. I see it a little too often :(

    The thing is that Erlang is supposed to be great fit for something like Rabbit. But the distributed database underneath, Mnesia, which is also written in Erlang, isn't good enough. But then, it's not alone. Galera cluster for MySQL/MariaDB can't recover from full restart without administrator help either, and that's written in C or C++, so it's more like the problem is hard and Erlang can't help much with that.



  • @Arantor said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    JIRA isn’t awful because it’s written in Java, it’s awful because it’s badly written

    It's still better than most other tools for that problem space.

    @Arantor said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    the fact no non-terrible tools exist in a given space is not, as far as I’m concerned inherently because the languages used to write said tools produced the bad tools

    It's usually because the problem space is much bigger than it looks, and the tools are either poorly analysed, or are trying to be too many things for too many people and it does not work well.

    Which is the case of Jira too. It has a lot of features to cover everybody's workflow and everybody uses it slightly differently and that makes it too much of a mess.


  • Discourse touched me in a no-no place

    @Bulb said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Arantor said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    JIRA isn’t awful because it’s written in Java, it’s awful because it’s badly written

    It's still better than most other tools for that problem space.

    The big problem seems to come once you add in complex (human) workflow management to the issues. That's because humans don't stick to workflows, or at least not to the ideal ones programmed into the tool, and the workflows are always local customizations done by a sysadmin and so are a bit half-assed (because they've got other shit to do too).

    Simpler tends to be better.



  • @Bulb said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    Which is the case of Jira too. It has a lot of features to cover everybody's workflow and everybody uses it slightly differently and that makes it too much of a mess.

    This, plus extensible configurability...

    In my previous job, our Head of Software Development had this story from some "Agile Conferences" he attended (as a speaker): people came up to him and asked

    🧝♂ What's the tool you use for tickets? Looks nice, snappy and useful. We have just this <censored> JIRA and it sucks!
    🧙♂ It's JIRA, actually.
    🧝♂ What?? Our JIRA is different!
    🧙♂ As a Head of Software Development, I spend third of my time working on the JIRA configuration so our developers don't have to waste time fighting it. What does your Head of Software Development do?
    🧝♂ errr... Sitting on meetings, I guess?


  • BINNED

    @Kamil-Podlesak said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    As a Head of Software Development, I spend third of my time working on the JIRA configuration

    🤯 🔫

    Sounds like getting promoted into :sudoku:.



  • @dkf said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Bulb said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Arantor said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    JIRA isn’t awful because it’s written in Java, it’s awful because it’s badly written

    It's still better than most other tools for that problem space.

    The big problem seems to come once you add in complex (human) workflow management to the issues. That's because humans don't stick to workflows, or at least not to the ideal ones programmed into the tool, and the workflows are always local customizations done by a sysadmin and so are a bit half-assed (because they've got other shit to do too).

    Simpler tends to be better.

    Our workflows seem mostly saneish—though sometimes it's not clear whether the ready-for-merge/ready-for-testing states we have in stories, but not tasks, will be needed (and therefore whether to make it a task or story).

    On the other hand what I would like if it did support is if it checked the “blocks”/“blocked by” links and prevent moving a blocked ticket above the ticket that blocks it.


  • Notification Spam Recipient

    @Arantor said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Dragoon I’m sure something exists in Java that is not awful. I’m not I have personally seen it but I’m sure it exists…

    The problem with Java it's built by a corporate for corporate. From my experience with corporate, they tend to hire three types of developers. Magpies, outsourced and over engineerers. The rest of the ecosystem has been an immune response or containment for the latter two groups created by the first group.

    I suspect C# falls into this category too, but you probably see fewer magpies because Microsoft controls 80% of it and there's less rope to hang yourself with. I'm not all that interested in C# though. It's struck me as more of the same. C and Go might be a chance for sanity, but I have my doubts about C.


  • BINNED

    @DogsB said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    C and Go might be a chance for sanity

    :laugh-harder:



  • @DogsB said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    Magpies

    :sideways_owl: They steal shiny bad ideas?

    @DogsB said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    The rest of the ecosystem has been an immune response or containment for the latter two groups created by the first group

    My impression is instead that a lot of the ecosystem is rather created by the overengineers (a.k.a architecture astronauts).

    @DogsB said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    C and Go might be a chance for sanity, but I have my doubts about C.

    C is way too low-level. You'll spend inordinate amounts of time thinking about allocating and deallocating buffers and shuffling bits around with it. It might help keep your feet to the ground to prevent becoming another architecture astronaut.

    Go might be a good replacement for Java. I find it a bit self-inconsistent (e.g. structs are by-value, but arrays are always by-reference) and of course it introduced the harmful go statement, but now it finally grew a pair of generics it might be a good balance of simple and effective for those simplish services over databases so common in corporate information systems. It does not seem to be general though—e.g. I haven't seen it used for things like GUI applications or on mobile or embedded etc.



  • @Bulb said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    of course it introduced the harmful go statement[blagoblag link],

    That blog post could be like 1/10th (or less) of the length and still get its point across, preferably without all the BS hyperbole.

    I'll save everybody else the time in skimming it: tl;dr: go appears to spawn a (lightweight) thread that goes off and does stuff and there's no guarantee that it'll ever stop doing stuff. The proposed solution is to make a wrapper joins spawned threads (lightweight or not) before leaving the scope in which the wrapper exists.

    Filed under: Meh.


  • Considered Harmful

    @Bulb said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    Go might be a good replacement for Java. I find it a bit self-inconsistent (e.g. structs are by-value, but arrays are always by-reference) and of course it introduced the harmful go statement,

    TBF, it came up with a sightly more concise way of doing almost the same harmful thing everybody else has been doing for about 30 years anyway. "Almost" because they go to some lengths to avoid the biggest holes in your feet by encouraging channel-based synchronization although they'll let you pull the trigger anyway if you insist.


  • ♿ (Parody)

    @Bulb said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Arantor said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    JIRA isn’t awful because it’s written in Java, it’s awful because it’s badly written

    It's still better than most other tools for that problem space.

    It's a common pattern that you see everywhere: This thing is the worst but all the alternatives are worse.



  • @LaoC said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Bulb said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    Go might be a good replacement for Java. I find it a bit self-inconsistent (e.g. structs are by-value, but arrays are always by-reference) and of course it introduced the harmful go statement,

    TBF, it came up with a sightly more concise way of doing almost the same harmful thing everybody else has been doing for about 30 years anyway.

    Indeed, and the article criticises it everywhere, just using the go statement as a symbol, because it's a special syntax rather than a normal function or object constructor like in most other languages.

    "Almost" because they go to some lengths to avoid the biggest holes in your feet by encouraging channel-based synchronization although they'll let you pull the trigger anyway if you insist.

    The channel-based synchronization provides a false sense of security. A lot of code sends a message, but then accesses shared state anyway, so if the synchronization via message is wrong, it causes all the same problems as locks would, and because there is no ownership tracking, it's easy to accidentally share the messages themselves too.

    @cvi said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    go appears to spawn a (lightweight) thread that goes off and does stuff and there's no guarantee that it'll ever stop doing stuff.

    Which is no different from creating a Thread object in, say, Java or C#, just integrated into the language for more convenient shooting yourself in the feet.

    The proposed solution is to make a wrapper joins spawned threads (lightweight or not) before leaving the scope in which the wrapper exists.

    A library function that spawns some threads, shares some context with them and then fails to join them is not a too common source of bugs, but they are difficult to debug when they happen. Restricting the threads to some scope and automatically joining them at the end is a useful tool to have to avoid it.

    Note that C++ tries to help a bit here. The thread destructor errors out when the thread was neither joined nor explicitly detached, at least pointing out mistakes in thread management.


  • Discourse touched me in a no-no place

    @cvi said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    I'll save everybody else the time in skimming it: tl;dr: go appears to spawn a (lightweight) thread that goes off and does stuff and there's no guarantee that it'll ever stop doing stuff. The proposed solution is to make a wrapper joins spawned threads (lightweight or not) before leaving the scope in which the wrapper exists.

    So because someone can't really conceive of ever needing to spawn threads that go and do their thing for a long time, the entire language is trash? That really does sound more like a lack-of-imagination problem on the behalf of the blogger. Or rather he couldn't really conceive of it so he wrote his own "solution" and is shilling it; scope-binding is very much a solution for resource management only when you've got short-lived resources, and when you have resource usage patterns that don't match the natural scopes exactly it all gets really nasty. (And he's encouraging people to use :disco:🐎 to discuss it. :doing_it_wrong: No thank you!)

    The amount of shit that people do to try to avoid some patterns of memory management is pretty horrible. (Same as they also go to a lot of effort to avoid exceptions, despite their alternatives imposing a lot greater impact on programmers and rarely being actually any better because real runtime faults like integer-divide-by-zero just terminate the process immediately.) The biggest single problem with a garbage collector is that it doesn't live nicely with any other way of managing memory (especially including other GCs!)


  • BINNED

    @dkf said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    So because someone can't really conceive of ever needing to spawn threads that go and do their thing for a long time, the entire language is trash?

    No, it's trash because it's made by (and named after) Google. :mlp_smug:


  • Discourse touched me in a no-no place

    @topspin If that was his argument, we wouldn't have been debating it.





  • @dkf said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @cvi said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    I'll save everybody else the time in skimming it: tl;dr: go appears to spawn a (lightweight) thread that goes off and does stuff and there's no guarantee that it'll ever stop doing stuff. The proposed solution is to make a wrapper joins spawned threads (lightweight or not) before leaving the scope in which the wrapper exists.

    So because someone can't really conceive of ever needing to spawn threads that go and do their thing for a long time, the entire language is trash? That really does sound more like a lack-of-imagination problem on the behalf of the blogger.

    He didn't say the entire language is trash. In fact he almost doesn't talk about go language at all. All go does is encourage this a little more, but pthread_create, std::thread, java.lang.Thread.start etc. are all the same. And Java does

    Or rather he couldn't really conceive of it so he wrote his own "solution" and is shilling it; scope-binding is very much a solution for resource management only when you've got short-lived resources,

    Scope-binding is a solution for resource management for all sorts of resource lifespans. There is always a scope that covers the whole runtime of the application.

    and when you have resource usage patterns that don't match the natural scopes exactly it all gets really nasty.

    When you have proper RAII, destructor composition fills in the rest. Unfortunately RAII-based scoped threads died in the leakpocalypse.

    But it does not say the threads need to be bound to the scope, just limited by it. You can always spawn threads in the main “nursery”. You just have to be explicit about it, which I consider definitely good.

    (And he's encouraging people to use :disco:🐎 to discuss it. :doing_it_wrong: No thank you!)

    Everybody and the 🐎 seems to be using :disco:🐎 by now. Get over it…

    The amount of shit that people do to try to avoid some patterns of memory management is pretty horrible. (Same as they also go to a lot of effort to avoid exceptions, despite their alternatives imposing a lot greater impact on programmers and rarely being actually any better because real runtime faults like integer-divide-by-zero just terminate the process immediately.) The biggest single problem with a garbage collector is that it doesn't live nicely with any other way of managing memory (especially including other GCs!)

    The biggest problem with a garbage collector is that it doesn't extend to managing other kinds of resources well. And once you've got a garbage collector, destructors (if they even exist) stop composing, because the objects no longer unambiguously own their members, so you don't get proper RAII, which you could use for managing other resources.


  • BINNED


  • ♿ (Parody)

    @topspin yeah. Should be "leakpocalypse."



  • @topspin It's a semi-official name for the realization that it cannot be guaranteed that objects will be dropped at the end of their lifetime because there are safe ways to leak them e.g. by constructing a cycle of Rcs and therefore safe code may not rely on drop for ensuring safety—which is exactly what the first version of scoped threads was doing.


  • Discourse touched me in a no-no place

    @Bulb said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    The biggest problem with a garbage collector is that it doesn't extend to managing other kinds of resources well. And once you've got a garbage collector, destructors (if they even exist) stop composing, because the objects no longer unambiguously own their members, so you don't get proper RAII, which you could use for managing other resources.

    I prefer different approaches to handle the "this object is in its operational phase" stuff, such as using in C# or with in Python. The important thing is that there is a visual indication that this object may be subject to some significant operation ("closing") at the end of that block of code; the explicitness of this (language keywords!) is what I like.



  • @dkf The explicitness is nice indeed. But the automatic composition is nicer.

    In C++ if you create an object that has three files and a database connection as value members, the destructor of the object will automatically call destructors of the files and database connection, enforcing they'll be closed. But Java, C# or Python don't have value members, so the closeable or context manager or whatever interfaces don't do this automatically, which creates risk of getting it wrong.



  • @Bulb RAII is a bizarre abstraction inversion that handles one specific case of try/finally in a language that doesn't have try/finally, and forces you to reimplement try/finally on top of RAII for any other usage. Java, C#, and Python all have try/finally, so they don't need RAII.


  • BINNED

    @Mason_Wheeler said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Bulb RAII is a bizarre abstraction inversion that handles one specific case of try/finally in a language that doesn't have try/finally, and forces you to reimplement try/finally on top of RAII for any other usage. Java, C#, and Python all have try/finally, so they don't need RAII.

    Wrong. C++/Rust have RAII, so they don't need try/finally. Of course, you could do try blocks if you desperately wanted to do things wrong.


  • ♿ (Parody)



  • @Mason_Wheeler said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Bulb RAII is a bizarre abstraction inversion that handles one specific case of try/finally in a language that doesn't have try/finally, and forces you to reimplement try/finally on top of RAII for any other usage. Java, C#, and Python all have try/finally, so they don't need RAII.

    Wrong. try/finally is a laughable attempt to address a small fraction of the problem RAII addresses much more completely. Even try-with-resources/using/with still solves only part of the issue RAII does.


  • ♿ (Parody)

    @Bulb said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Mason_Wheeler said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Bulb RAII is a bizarre abstraction inversion that handles one specific case of try/finally in a language that doesn't have try/finally, and forces you to reimplement try/finally on top of RAII for any other usage. Java, C#, and Python all have try/finally, so they don't need RAII.

    Wrong. try/finally is a laughable attempt to address a small fraction of the problem RAII addresses much more completely. Even try-with-resources/using/with still solves only part of the issue RAII does.

    He's like a walking Blub demonstration.


  • BINNED

    @Bulb said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @topspin It's a semi-official name for the realization that it cannot be guaranteed that objects will be dropped at the end of their lifetime because there are safe ways to leak them e.g. by constructing a cycle of Rcs and therefore safe code may not rely on drop for ensuring safety—which is exactly what the first version of scoped threads was doing.

    What part is safety critical about leaking threads? I would guess something like leaking references to local variables into a closure that outlives them, but I'd also assume that Rust's general lifetime rules already prevent this.
    And how did this get fixed in the current version of scoped threads?



  • @topspin Normally a thread requires arguments to be 'static because no life-time limit can be guaranteed for it. So scoped threads were introduced to allow an 'a scoped thread to accept arguments that are 'b: 'a. But that relied on drop running at end of 'a and it turned out that it can be guaranteed if the user can instantiate the object and/or move it around. So it was changed to a scope function (still experimental) that runs a closure and now threads spawned inside can take any references that outlive the scope function itself.

    … but of course that limits the usefulness somewhat, because now you can't have an object that has a scoped thread as an attached resource, unlike files, connections and similar which you can embed in a structure.



  • @boomzilla said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Bulb said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Mason_Wheeler said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Bulb RAII is a bizarre abstraction inversion that handles one specific case of try/finally in a language that doesn't have try/finally, and forces you to reimplement try/finally on top of RAII for any other usage. Java, C#, and Python all have try/finally, so they don't need RAII.

    Wrong. try/finally is a laughable attempt to address a small fraction of the problem RAII addresses much more completely. Even try-with-resources/using/with still solves only part of the issue RAII does.

    He's like a walking Blub demonstration.

    ...says the person who apparently doesn't know that "abstraction inversion" is a single concept made up of two words.



  • @Bulb said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Mason_Wheeler said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Bulb RAII is a bizarre abstraction inversion that handles one specific case of try/finally in a language that doesn't have try/finally, and forces you to reimplement try/finally on top of RAII for any other usage. Java, C#, and Python all have try/finally, so they don't need RAII.

    Wrong. try/finally is a laughable attempt to address a small fraction of the problem RAII addresses much more completely. Even try-with-resources/using/with still solves only part of the issue RAII does.

    grid.DisableRefresh();
    try
       LoadLotsOfData(grid, data);
    finally
       grid.EnableRefresh();
    

    Is it even remotely possible to implement this in a RAII-based language without creating a massive :wtf: of extra code including a whole other class and a gratuitous extra scope so you can shoehorn basic try/finally behavior into the RAII paradigm?


  • ♿ (Parody)

    @Mason_Wheeler said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @boomzilla said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Bulb said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Mason_Wheeler said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Bulb RAII is a bizarre abstraction inversion that handles one specific case of try/finally in a language that doesn't have try/finally, and forces you to reimplement try/finally on top of RAII for any other usage. Java, C#, and Python all have try/finally, so they don't need RAII.

    Wrong. try/finally is a laughable attempt to address a small fraction of the problem RAII addresses much more completely. Even try-with-resources/using/with still solves only part of the issue RAII does.

    He's like a walking Blub demonstration.

    ...says the person who apparently doesn't know that "abstraction inversion" is a single concept made up of two words.

    I'll take my awkward quoting over your dumb and completely unsubstantiated opinions any day.




  • ♿ (Parody)

    @Mason_Wheeler said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @boomzilla said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    completely unsubstantiated

    ☝

    Yeah, you posted another dumb thing while I was posting. I have no idea why you would connect that to RAII, other than that you've fixated on the try-finally construct.



  • @Mason_Wheeler Don't forget the try block is also a scope. There needs to be a helper … which C++ is notoriously bad at providing, so you'd probably abuse unique_ptr for that.


  • Considered Harmful

    RAII, try..finally... :why_not_both:

    I don't see why they're mutually exclusive, or that one is a completely effective replacement for the other.



  • @error They are exclusive in practice because IIRC no language has both. Languages that have RAII (and I only know about two) use helpers for the rare case ad-hoc finally is needed, while languages that have finally have introduced some kind of try-with-resources to make it less error-prone, but don't have RAII, which is hard to add to languages without notion of explicit unique ownership.


  • Discourse touched me in a no-no place

    @error said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    RAII, try..finally... :why_not_both:

    I don't see why they're mutually exclusive, or that one is a completely effective replacement for the other.

    When you have RAII, your natural mode of thought is to try to find scopes that are exactly the lifetime you want, and to require the code in those scopes to become expert on the objects concerned. In particular, the scope needs to know that it must create the object concerned and exactly how to do so; any sort of complex conditionality there and you're into making all sorts of delegate objects that you hand off the real construction and/or lifetime management to.

    With the explicit keyword approach, you make the scope right there (or usually; you can make the object before too). Things are still a bit messy with conditional deletion. An example of why the Python approach is good is that it doesn't just model deletion of files, but can also handle stuff like database transactions:

    with db_cursor:
        # Do the operations in here; committing on finish, or
        # rolling back if an exception is thrown
    

    It's not perfect (there's no facility to restart the transaction if the DB tells you to do so) but it is easy to get basic things going reasonably correctly.

    Languages like Lisp, Ruby and Tcl use special-purpose constructs for this stuff, which are more like what you'd want to do by passing lambdas in at least Java and C#. It's pretty rare for people to go that route though, for some reason, which I suspect is to do with people simply not thinking like that. (I'm less sure about C++; I believe it has this sort of thing?)


  • BINNED

    @Mason_Wheeler said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Bulb said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Mason_Wheeler said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Bulb RAII is a bizarre abstraction inversion that handles one specific case of try/finally in a language that doesn't have try/finally, and forces you to reimplement try/finally on top of RAII for any other usage. Java, C#, and Python all have try/finally, so they don't need RAII.

    Wrong. try/finally is a laughable attempt to address a small fraction of the problem RAII addresses much more completely. Even try-with-resources/using/with still solves only part of the issue RAII does.

    grid.DisableRefresh();
    try
       LoadLotsOfData(grid, data);
    finally
       grid.EnableRefresh();
    

    Is it even remotely possible to implement this in a RAII-based language without creating a massive :wtf: of extra code including a whole other class and a gratuitous extra scope so you can shoehorn basic try/finally behavior into the RAII paradigm?

    It's only "extra code" if your operation is completely ad-hoc and you only do it once. That happens, but it really shouldn't happen often. For anything that's used more than twice, this is DRY and reduces extra code.

    Depending on your needs you'd have at least the following options:


    RefreshDisabler d(grid);
    LoadLotsOfData(grid, data);
    

    Takes a few lines to write a weird RAII class, but if this is actually a sensible operation you do several times, then abstract it away. No more chance of accidentally forgetting to write that finally, always correct by construction. Less code to use it, too.
    Don't complain about the name, The Kingdom Of Nouns comes from Java land.


    grid.DisableRefresh();
    ScopeGuard g([&]{ grid.EnableRefresh(); });
    LoadLotsOfData(grid, data);
    

    Ad-hoc solution for one-off problems. Requires understanding the idiom, but it's not complicated. You're going to argue that it wrongly inverts control flow, but it's actually a feature because it provides better locality between the pair of enable/disable.


    grid.DisableRefresh();
    try {
       LoadLotsOfData(grid, data);
    } catch (...) {
       grid.EnableRefresh();
       throw;
    }
    grid.EnableRefresh();
    
    

    There's your try/finally block. It's ugly in that it repeats one line, so is actually longer this time. But they, you asked for one and so if you really want it, you got it.


  • Considered Harmful

    @dkf said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    In particular, the scope needs to know that it must create the object concerned and exactly how to do so; any sort of complex conditionality there and you're into making all sorts of delegate objects that you hand off the real construction and/or lifetime management to.

    This isn't that bad - the construction can be orthogonalized from the lifecycle via the loan pattern, and m.m. Closeable is far and away the most common lifecycle.


  • Considered Harmful

    @topspin this would be preferable using the grid's locked state as a try-with resource containing a counted reference.

    Even without try-with-resources, with an actual finally enablement need not repeat. The only point of a shared countable resource would be to use the open/close count zeros to lock/unlock for real.


  • Considered Harmful

    @Mason_Wheeler ⬆ yes. You can combine RAII directly with try/finally, even, as long as construction and destruction facades can see the reference count (or are only called on zeros), and paired actions map directly to open/close semantics.



  • @topspin said in Functional programming rah! OOP nah! Or how to know you're a zealot:

     grid.DisableRefresh();
    ScopeGuard g([&]{ grid.EnableRefresh(); });
    LoadLotsOfData(grid, data);
    

    Ad-hoc solution for one-off problems. Requires understanding the idiom, but it's not complicated. You're going to argue that it wrongly inverts control flow, but it's actually a feature because it provides better locality between the pair of enable/disable.

    If you're not too adverse to macros, you can make it

    grid.DisableRefresh();
    ON_SCOPE_EXIT
    {
         grid.EnableRefresh();
    };
    LoadLotsOfData(grid, data);
    

    (I tend to prefer that; the random g variable that you need to introduce is icky IMO.)

    I technically wouldn't mind a "proper" try/finally construct in C++. It comes in handy the few times you need it, and it's pretty easy to understand.

    That said, I'd probably vote for a more convenient/less cluttered standardized way of doing the scope_guard over the try/ finally.


  • BINNED

    @cvi said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    I technically wouldn't mind a "proper" try/finally construct in C++. It comes in handy the few times you need it, and it's pretty easy to understand.

    Yes, but it's really low on the list of pain points. You just don't need it very often, if at all.
    It took them two decades to get basic methods into string, so this one really isn't as important as the argument makes it out to be.



  • @Mason_Wheeler said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Bulb said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Mason_Wheeler said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    grid.DisableRefresh();
    try
       LoadLotsOfData(grid, data);
    finally
       grid.EnableRefresh();
    

    Is it even remotely possible to implement this in a RAII-based language without creating a massive :wtf: of extra code including a whole other class and a gratuitous extra scope so you can shoehorn basic try/finally behavior into the RAII paradigm?

    In a language with RAII, and if the author of that UI framework understood that, the code would look like:

    auto gridUpdate = grid.Updater();
    LoadLotsOfData(gridUpdate, data);
    

    and because you have a function to load those data, it could be abbreviated to:

    LoadLotsOfData(grid.Updater(), data);
    

    That is much better interface design, because it guides you to do it right. You have this helper object, but the user does not have to remember to use it, because the mutation methods are on the helper, can't forget to make it exception-safe by not putting the closing call in a finally, can't forget to make the closing call altogether.

    In languages that have try-with-resources/using/with the interface could be

    with grid.Updater() as gridUpdate:
        LoadLotsOfData(gridUpdate, data)
    

    which is much closer. Or in languages without a with statement, but with good closure support it could be

    grid.Updater(|gridUpdate| {
        LoadLotsOfData(gridUpdate, data);
    }
    

    Compared to RAII this is a bit more explicit, which is usually good, but does not compose as well when you want to build complex resource guards from multiple simper ones.


  • BINNED

    @Bulb I was answering a bit more closely/literally, but yours is much nicer. I like that one-liner.
    It actually has the “pit of success” that he mentioned earlier about C# design.



  • @Bulb said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    You have this helper object, but the user does not have to remember to use it, because the mutation methods are on the helper, can't forget to make it exception-safe by not putting the closing call in a finally, can't forget to make the closing call altogether.

    You're "solving" problems that don't exist. Like parens or braces, you write the start and the end together and then add the material between them; that's just how code like this is written. This is a fairly common pattern in GUI programming, and I've literally never seen it mismatched. Not once.


  • ♿ (Parody)

    @Mason_Wheeler said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    @Bulb said in Functional programming rah! OOP nah! Or how to know you're a zealot:

    You have this helper object, but the user does not have to remember to use it, because the mutation methods are on the helper, can't forget to make it exception-safe by not putting the closing call in a finally, can't forget to make the closing call altogether.

    You're "solving" problems that don't exist. Like parens or braces, you write the start and the end together and then add the material between them; that's just how code like this is written. This is a fairly common pattern in GUI programming, and I've literally never seen it mismatched. Not once.

    Hey, remember this morning when we were making fun of you for shit like this?

    Good times.


Log in to reply