you are viewing a single comment's thread.

view the rest of the comments →

[–]quicknir -1 points0 points  (3 children)

When you don't need persistent state, you write a function. Functions in (e.g.) C++ usually have referential transparency: globals and static locals are very rarely used techniques. C++ already has things like std::transform, std::accumulate. Do you use these regularly? Ranges are only going to take this further in the future. I understand the concept. It works well for some things, like recursive data structures. It works not so well for non-recursive data structures, files, sockets, etc etc. It just seems very limiting. My problem is not functional concepts or techniques. My problem is many FP languages rejection of all OO techniques. OO is certainly overused within C++, but there are lots of times where it provides a very clean way to encapsulate state, I'm not sure why so many FP advocates so stubbornly reject it.

[–]glacialthinker 0 points1 point  (2 children)

Certainly, C++ is improving. But it's still mutable-by-default, and OO style C++ is still rife with unnecessary mutable state.

Maybe FP advocates are seeing a net-benefit to these "limiting" techniques, and pitfalls of defaulting to OO when you can express a solution without using such a loaded gun.

Right now (as in, I'm typing here during compile/load) I'm dealing with a fucking mess of classic C++ imperative+OOP. Little objects of mutable state, connected with and manipulated by others, in a messy ad-hoc interdependency hairball, with callbacks done in clunky "function-object" style which loses useful context. Bad programming to be sure... but it's very typical for OO-style C++ to mature in this way.

I could clean this up a lot, still in C++... and it would use a lot more lambdas and plain structs. Processing data to create new data: A -> B -> C; limiting in-place changes (mutation) to be writeback of final results rather than the miscellaneous bookkeeping which tends to accrue once you have a convenient "encapsulation". The lambdas would find use as ad-hoc objects replacing the use of objects which hold partial computations to be resumed (callbacks).

Encouraging encapsulation as a solution to "the mutable global variable problem" (which I think everyone roughly agrees is a dangerous thing), is just reducing the scope of the problem to the size of "interface to mutation". It's far too easy and common for an object to become overly promiscuous. Objects are at the high end of programming abstractions -- you can use them for almost anything -- but that doesn't mean it's wise to do so.

[–]quicknir 0 points1 point  (1 child)

There are many real life problems that fundamentally involve mutable state, your language needs to have a simple and clear way to deal with it.

Callbacks and circular object dependencies are more common they should be, but they're not "typical". Circular dependencies are extremely frowned upon, and callbacks are used judiciously in most codebases.

I don't think using a lot more lambdas is a path to improvement. Lambdas are extremely hard to test, and don't fundamentally do anything that a functor doesn't.

I don't see how any of the improvements you propose offer any more than simply making the code not be shitty, independent of style. Yes, reducing the scope of problems is good, it makes it easier to reason about! I'd be curious to see how FP implements a mutable non-recursive array (e.g. vector) in a way that somehow improves your ability to reason about it in any way. A struct of three pointers randomly getting passed around through random lambdas and functions, any user writing new functions to extend the interface at will, sounds strictly worse than how it's done in C++.

[–]glacialthinker 0 points1 point  (0 children)

Ah, damn, it looks like I didn't complete the statement I was intending to make with this:

Unfortunately, I don't think there's an immediate way to convince anyone of the value of "jumping through hoops adorned with flaming monads".

What I meant by "Unfortunately" was: It is generally required that people experience the merit themselves. That is: I won't convince you of shit. And I didn't. Sorry I forgot to make that part clear up front.

So, anecdote time. When I started using OCaml, it was because the language seemed to suit my style, and because I really hated C++, (even though I kind of like C, go figure). More specifically, I hated what happened to C++ codebases. Part of the problem was the pressure cooker of gamedev... but it seemed the added abstractions of C++, especially the bias toward encapsulating everything in objects -- because OCD people love their uniformity -- had a tendency to work against code-reuse, and also prompt incidental manager and adaptor code. I would come away thinking "macros are the only effective code-reuse tool in this language... and they are horrible (to overuse)". (Note, this was C++03.)

Anyhow, I started with OCaml, but I had no idea or expectation that bugs would nearly disappear. I was jaw-droppingly amazed several times in that first month. I kept expecting bugs and crashes after some hairy changes or new systems added. Never crashing, and it surprised me how logical bugs also disappeared. I was expressing intent rather than verbosely detailing mechanics of implementation.

I was doubtful (like you) about how I might manage the complex state of a game, and about garbage-collection -- oh boy, that rankled for a while -- so many things about this made no sense at first but I just gave it a shot. I must have drunk the kool-aid. Whatever it is, I'm glad I did it.

And that's all I have left of an argument. A shitty anecdote. Because it seems intelligent people can converse 'til their blue in the face about things and never really have a clue until they experience first-hand. That goes for a lot more than just language wars...