On Wed, Feb 14, 2018 at 06:43:34PM +0000, Mark via Digitalmars-d wrote: > On Wednesday, 14 February 2018 at 09:39:20 UTC, Luís Marques wrote: > > It seems that someone once again rediscovered the benefits of > > component programming, in the context of OOP, but (as usual) without > > the more mathematical and principled approach of something like > > ranges and algorithms: > > > > [...] > > Luna [1], a new programming language that was recently mentioned on > Reddit, also appears to take this "flow-oriented design" approach. > It's purely functional, not OO, and I'm curious to see how it evolves. [...]
This so-called "flow-oriented design" is hardly a new idea. It has been around since Lisp and the Unix command-line. And of course, D ranges benefit greatly from it. :-) The power of this idiom comes from the way it confers symmetry of API across diverse components. In the traditional situation, if a library X exports methods foo() and goo(), and another library Y exports methods bar() and car(), then you cannot simply plug Y in where X is used, because their APIs are incompatible, and you need to write proxying code to bridge the "API impedance mismatch" between them. For sufficiently complex APIs, this is highly non-trivial, and often leads to a lot of boilerplate like proxy objects, shim code, and other such artifacts. For example, if X calls Y and Y calls Z, and X, Y and Z all have different APIs, then removing Y from between X and Z will require rewriting parts of X so that it will fit with Z's API. And often this may not even be possible without essentially rewriting the entire subsystem. However, under the component programming model, both X and Y would be unified under a single, universal API: input and output. This makes their APIs symmetric: they become interchangeable with each other, from the API point of view. Now you can insert/remove components in the middle of a pipeline without needing to rewrite everything around them, because the symmetry of the API ensures that the new component(s) will simply "just fit", no matter what order you attach them. Of course, the limitation of this approach is that it only applies to problems that are amenable to be modelled as a linear pipeline. For more complex problems that require non-linear algorithms, it will be necessary to use some other kind of API to accomplish what's needed. Though, the linearizable pieces of such algorithms can still benefit from the component / pipeline approach. But even in non-linear problems, if you can boil down the problem to its barest essentials and unify the APIs of the relevant modules that way, then you can still reap the benefits of having an API that's symmetric across different modules. One example of this is user-defined numerical types: in languages that support operator overloading, the expression syntax serves as the unifying "API"; as long as your custom numeric type conforms to this API (i.e., it is symmetric w.r.t expression syntax), you can simply just "plug it in" and it will Just Work(tm), for the most part (complications like floating-point idiosyncrasies aside). Finding this sort of symmetry in API design is a hard problem in general, though. While almost everyone can agree on "input + output" in the pipeline model, a universal API that encapsulates more complex tasks may not be so obvious. And as long as you can't get everyone to agree to it, it will be difficult to interoperate the respective modules. This is why API design is very hard. :-) T -- "Real programmers can write assembly code in any language. :-)" -- Larry Wall
