3 ms·
I think you over-estimate the share of combinatorial problems in software. There’re a lot of them indeed. But outside niches like compilation and text parsing,
by Const-me 10y ago
I think you over-estimate the share of combinatorial problems in software.
There’re a lot of them indeed. But outside niches like compilation and text parsing, they are far from majority.
Some problems are bandwidth-bound, like photoshop: if you have 1GB image, you have to process the complete data set no matter the combinatory.
Other problems are IO bound, like databases: if user wrote a query that need to do sequential scan over a table, you’ll have do to that I/O.
For both classes of problems, OOP is just fine.
Another thing is, OOP is better at explicitly managing the state. If you design an app your way, viewing your code as reduction over previous inputs, I expect you’ll struggle implementing state management functionality like undo/redo stacks, and saving/loading your stuff in these formats and protocols.
- javajosh 10y agoThe combinatorial problem is universal. In OOP we organize our processes using objects. Classes (collections of methods, which are collections of statements) are runtime-invariant. In the steady-state, your process handles input as a method invocation (the main(String[]) of a CLI program or the handleRequest(req) method of a servlet, for example). In both cases, the thread associated with the input travels through your code, going deeper, then shallower, deeper again (the depth is the stack frame count). Each time we go deeper (each time we conditionally invoke a method) we are solving a combinatorial problem across our own code-base. We are effectively crafting, at runtime, a unique program (concrete list of statements) to deal with this input. We don't normally learn OOP this way, or think of it this way, but it really is what we are doing. But there are some benefits to thinking in this way. For example, what is an interface? Like all indirection, it's like lubricant. It decouples one set of statements from another, and makes it easier to substitute (or slide) out the second set. And a factory? Well, it produces bundles of statements that conform to an interface. But since we are strongly-typed, the factory is only selecting from a finite set. (In fact, the factory pattern is a perfect example of how OOP practitioners are doing combinatorics all the time, but we don't know it, and we do it crudely.)
- Const-me 10y ago> In OOP we organize our processes using objects We organize our state using objects. Many programs have to deal with complex mutable state. The extreme cases are OS kernel state, or game state in an AAA videogame. For such programs, OOP is the only approach allowing people to reason about what’s going on inside these things. If you don’t have complex mutable state, other approaches might become more practical. One example is compilers; their state is mostly immutable. Another is pure computations without state, especially symbolic computations, like what happens in Maple. > the thread associated with the input travels through your code, going deeper, then shallower, deeper again In many programs, input handling is minor part of overall software complexity. Sometimes, most of the code don’t even run on the input thread. > decouples one set of statements from another, and makes it easier to substitute (or slide) out the second set. Interfaces abstract away arbitrary stuff (statements, data, hardware devices, etc.), providing strongly-typed API to use that stuff, and hiding complexity of the implementation. In some case (COM interfaces), the statements you’re selecting were written in another language, are implemented in another DLL being loaded on demand, run in different process that starts up on demand, or for DCOM even run on different machine. > since we are strongly-typed, the factory is only selecting from a finite set In C#, some class factories take your strongly-typed interface, take other parameters you pass to the factory, and implement the interface in runtime. Because there is infinite set of possible class-factory parameters, that makes the set of statements infinite as well. BTW, this approach is often used in .NET RPC libraries, like WCF.
- javajosh 10y agoIf you opened your mind only a little further you would see what I am saying, friend. All call chains are input driven. Attach a debugger to anything you've made and stop the program - at the top, is an input. The tick() methods in a AAA game are handling input - the input of a ticking clock. A long-running process, by definition, does not change state without input. "Process organization" means how the memory associated with the (operating system) process is divided - and that memory includes both (mutable, transient) object state and (immutable, invariant) program code. Your comments about code generation are pedantic, and I will ignore them, since only a very small fraction of code written in Java, C#, or any other high-level OOL does dynamic code generation, or even reflection (and for the very good reason that it breaks the invariants assured by the OOP paradigm). Unfortunately, OOP doesn't allow people to reason about complex systems, primarily because call-chains are totally unconstrained. An object can contain references to any other object held by the process, and this reference list is dynamic at runtime. In the presence of instantiation indirection it's even worse, even the concrete type of those references is unknown except at runtime. There are code smells, and then there are architecture smells. One big architecture smell is when the incidental complexity of a solution vastly outstrips the intrinsic complexity of the problem. I have never (and I've been doing this a very long time) seen an OOP code base less than 10x the complexity of the business problem it was built to solve. It's sad to me that instead of asking why, they keep blaming the programmer/architect and inventing new complexity to mitigate old complexity.