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:
@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.
-
@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?
-
@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 .
-
@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.
-
@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.
-
@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
-
@DogsB said in Functional programming rah! OOP nah! Or how to know you're a zealot:
Magpies
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.
-
@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.
-
@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.
-
@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 to discuss it. 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!)
-
@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.
-
@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:
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 to discuss it. No thank you!)
Everybody and the seems to be using 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.
-
@Bulb said in Functional programming rah! OOP nah! Or how to know you're a zealot:
leakocalypse
-
@topspin yeah. Should be "leakpocalypse."
-
@topspin It's a semi-official name for the realization that it cannot be guaranteed that objects will be
drop
ped at the end of their lifetime because there are safe ways to leak them e.g. by constructing a cycle ofRc
s and therefore safe code may not rely ondrop
for ensuring safety—which is exactly what the first version of scoped threads was doing.
-
@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# orwith
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 havetry/finally
, and forces you to reimplementtry/finally
on top of RAII for any other usage. Java, C#, and Python all havetry/finally
, so they don't need RAII.
-
@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 havetry/finally
, and forces you to reimplementtry/finally
on top of RAII for any other usage. Java, C#, and Python all havetry/finally
, so they don't need RAII.Wrong. C++/Rust have RAII, so they don't need
try
/finally
. Of course, you could dotry
blocks if you desperately wanted to do things wrong.
-
@Mason_Wheeler said in Functional programming rah! OOP nah! Or how to know you're a zealot:
RAII is a bizarre abstraction
-
@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 havetry/finally
, and forces you to reimplementtry/finally
on top of RAII for any other usage. Java, C#, and Python all havetry/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.
-
@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 havetry/finally
, and forces you to reimplementtry/finally
on top of RAII for any other usage. Java, C#, and Python all havetry/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.
-
@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
drop
ped at the end of their lifetime because there are safe ways to leak them e.g. by constructing a cycle ofRc
s and therefore safe code may not rely ondrop
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 ondrop
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 ascope
function (still experimental) that runs a closure and now threads spawned inside can take any references that outlive thescope
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 havetry/finally
, and forces you to reimplementtry/finally
on top of RAII for any other usage. Java, C#, and Python all havetry/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 havetry/finally
, and forces you to reimplementtry/finally
on top of RAII for any other usage. Java, C#, and Python all havetry/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 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?
-
@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 havetry/finally
, and forces you to reimplementtry/finally
on top of RAII for any other usage. Java, C#, and Python all havetry/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.
-
@boomzilla said in Functional programming rah! OOP nah! Or how to know you're a zealot:
completely unsubstantiated
-
@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.
-
RAII, try..finally...
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.
-
@error said in Functional programming rah! OOP nah! Or how to know you're a zealot:
RAII, try..finally...
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?)
-
@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 havetry/finally
, and forces you to reimplementtry/finally
on top of RAII for any other usage. Java, C#, and Python all havetry/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 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.
-
@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.
-
@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.
-
@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 thetry
/finally
.
-
@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 intostring
, 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 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.
-
@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.
-
@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.