www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Non-pipeline component programming

reply =?UTF-8?B?Ikx1w61z?= Marques" <luismarques gmail.com> writes:
I loved reading Walter's component programming article in Dr. 
Dobb's [0] in late 2012. I had missed H. S. Teoh's mid 2013 
article on calendar textual formatting using the component 
approach [1], but fortunately Ali brought it to my attention 
recently, and I also find it absolutely fascinating!

I think what's most interesting with Teoh's article (and I think 
that was Ali's point when he mentioned the article to me) is that 
the calendar example is not as an obvious target for the 
component approach, or at least that the design and 
implementation is not as obvious for someone new to that approach.

Now, while Teoh's example is much more complex than Walter's, 
both examples are for cases of pipelined problems (source -> 
filter1 -> filter2 -> sink). What I have been wondering during 
the last few days is how much this "component programming" 
approach could be applied to scenarios where you would normally 
have a jumble of objects. For instance, try to picture a game or 
simulation where spaceships fire at each other, pick up objects, 
communicate, and so on, or something like that. My instinct would 
be to code a solution which would be classified as typical OOP 
code. Would it be possible to come up with a solution that would 
be more in the spirit of "component programming"? Or are such 
solutions only practical/applicable for pipeline-like scenarios?

--

[0] 
http://www.drdobbs.com/architecture-and-design/component-programming-in-d/240008321

[1] 
http://wiki.dlang.org/User:Quickfur/Component_programming_with_ranges
Feb 03 2014
next sibling parent reply "Zoadian" <d d.de> writes:
We're currently working on an Entity Component System for D ( 
https://github.com/Zoadian/nitro)

Here some good explanations:

http://t-machine.org/index.php/2007/09/03/entity-systems-are-the-future-of-mmog-development-part-1/

http://www.richardlord.net/blog/what-is-an-entity-framework



On Tuesday, 4 February 2014 at 04:12:49 UTC, Luís Marques wrote:
 I loved reading Walter's component programming article in Dr. 
 Dobb's [0] in late 2012. I had missed H. S. Teoh's mid 2013 
 article on calendar textual formatting using the component 
 approach [1], but fortunately Ali brought it to my attention 
 recently, and I also find it absolutely fascinating!

 I think what's most interesting with Teoh's article (and I 
 think that was Ali's point when he mentioned the article to me) 
 is that the calendar example is not as an obvious target for 
 the component approach, or at least that the design and 
 implementation is not as obvious for someone new to that 
 approach.

 Now, while Teoh's example is much more complex than Walter's, 
 both examples are for cases of pipelined problems (source -> 
 filter1 -> filter2 -> sink). What I have been wondering during 
 the last few days is how much this "component programming" 
 approach could be applied to scenarios where you would normally 
 have a jumble of objects. For instance, try to picture a game 
 or simulation where spaceships fire at each other, pick up 
 objects, communicate, and so on, or something like that. My 
 instinct would be to code a solution which would be classified 
 as typical OOP code. Would it be possible to come up with a 
 solution that would be more in the spirit of "component 
 programming"? Or are such solutions only practical/applicable 
 for pipeline-like scenarios?

 --

 [0] 
 http://www.drdobbs.com/architecture-and-design/component-programming-in-d/240008321

 [1] 
 http://wiki.dlang.org/User:Quickfur/Component_programming_with_ranges
Feb 04 2014
next sibling parent reply "Francesco Cattoglio" <francesco.cattoglio gmail.com> writes:
On Tuesday, 4 February 2014 at 09:49:39 UTC, Zoadian wrote:
 We're currently working on an Entity Component System for D ( 
 https://github.com/Zoadian/nitro)

 Here some good explanations:
I've seen it too. I like it so far, because of the nice usage of templates, but right now I didn't gave it a try due to zero docs. The questions asked by Luìs probably has more to do with "if I'm iterating one entity/component at a time, how can I access two components at one time for my computations?" The easy way is just using the cartesian product of your range with itself. http://dlang.org/phobos/std_algorithm.html#cartesianProduct This way you get a range of tuples, so you get pairs made of each entity with all the other ones, and you can process that pair with your next filter function. After that I'm not really sure about what one could do. Another approach could be: pass (a copy of your) RA range to a filter, and use that to compute the effects for each entity. Bang, every entity is now processed.
Feb 04 2014
parent reply "Paul Freund" <freund.paul lvl3.org> writes:
On Tuesday, 4 February 2014 at 10:06:18 UTC, Francesco Cattoglio 
wrote:
 On Tuesday, 4 February 2014 at 09:49:39 UTC, Zoadian wrote:
 We're currently working on an Entity Component System for D ( 
 https://github.com/Zoadian/nitro)

 Here some good explanations:
I've seen it too. I like it so far, because of the nice usage of templates, but right now I didn't gave it a try due to zero docs.
We are currently working on the first "stable" version (and API). The master branch is the last working state but our current development is in the finalize_basics (https://github.com/Zoadian/nitro/tree/finalize_basics) branch, including a work in progress documentation. It should be ready to use and documented in a few days.
Feb 04 2014
parent "Francesco Cattoglio" <francesco.cattoglio gmail.com> writes:
On Tuesday, 4 February 2014 at 10:24:57 UTC, Paul Freund wrote:
 We are currently working on the first "stable" version (and 
 API). The master branch is the last working state but our 
 current development is in the finalize_basics 
 (https://github.com/Zoadian/nitro/tree/finalize_basics) branch, 
 including a work in progress documentation. It should be ready 
 to use and documented in a few days.
That's really good news! A friend of mine was trying to get an entity system working with a etc.c.sqlite3 "backend", but right now he isn't that much satisfied by it. The main reason for having it on DB was having every component on a separate table, and because we plan on having LOTS of entities (~1 Million) (a DB should be able to handle that). Throw in the mix "free saving of state on file" and you can see why he made this choice. Taking a better look at it, your store every entity in a separate table, too! If performance will be good, I think we might switch to nitro for our project. Besides, I don't think that sqlite performance is that great for the amount of granularity we need. Pretty sure there are just too many casts going around. (3 bytes integers??? ARE YOU SERIOUS?)
Feb 04 2014
prev sibling next sibling parent reply =?UTF-8?B?Ikx1w61z?= Marques" <luis luismarques.eu> writes:
On Tuesday, 4 February 2014 at 09:49:39 UTC, Zoadian wrote:
 We're currently working on an Entity Component System for D ( 
 https://github.com/Zoadian/nitro)

 Here some good explanations:

 http://t-machine.org/index.php/2007/09/03/entity-systems-are-the-future-of-mmog-development-part-1/

 http://www.richardlord.net/blog/what-is-an-entity-framework
After carefully reading the material, I think that the Entity System / Entity Component System (ECS) framework is indeed a good design, and provides a good answer for the specific example I was considering (spaceships, etc.). Still, I wonder how related to the (pipelined) component approach described by Walter and Teoh this ECS approach really is. Perhaps more specifically, I wonder if the ECS approach also allows using a lot of the std.algorithms, like was done in the pipelined component approach, and in the calendar example in particular. Also, I'm a bit lost with the Nitro code and how to use it. (Tangentially, two things that I was wondering while reading about the ECS: how important would it be to have non-polymorphic reference types, and why are we paying for the object monitor field when that approach seems to be outdated, especially in D)
Feb 05 2014
parent reply "Mike Parker" <aldacron gmail.com> writes:
On Wednesday, 5 February 2014 at 15:29:14 UTC, Luís Marques wrote:

 Still, I wonder how related to the (pipelined) component 
 approach described by Walter and Teoh this ECS approach really 
 is. Perhaps more specifically, I wonder if the ECS approach 
 also allows using a lot of the std.algorithms, like was done in 
 the pipelined component approach, and in the calendar example 
 in particular.
Conceptually, it's not related. The only similarity is the use of the word "component." An ECS is concerned with breaking traditional objects into disparate, composable parts. This can be combined with the concept of Data-Oriented Programming (DOP), to improve cache locality when iterating entities, by storing all components of the same type in a contiguous block of memory. Then rather than iterating an array of entities and calling a setFoo method, you can instead more efficiently iterate an array of Foos independent of each entity. The pipelined component concept is about chaining a set of operations to transform a dataset where the output of one transformation is the input to the next. Here, the "components" are not the data but the transformations, which can be swapped in and out, reordered, or added to and removed from the chain. A cursory look at Nitro suggests they are using an ECS with DOP. Given that they're providing ranges for iterating the data, it will fit into D's range-based component pipelines (std.algorithm and such).
Feb 05 2014
parent reply "Meta" <jared771 gmail.com> writes:
On Thursday, 6 February 2014 at 05:27:22 UTC, Mike Parker wrote:
 A cursory look at Nitro suggests they are using an ECS with 
 DOP. Given that they're providing ranges for iterating the 
 data, it will fit into D's range-based component pipelines 
 (std.algorithm and such).
That seems cool. I can only imagine the possibilities... auto shipComponents = components.filter!(a => a.hasComponent!SpaceShip); foreach (i, spaceship; taskPool.parallel(shipComponents)) { spaceship.name = format("Ship %i", i); }
Feb 06 2014
parent reply "Paul Freund" <freund.paul lvl3.org> writes:
On Thursday, 6 February 2014 at 13:47:22 UTC, Meta wrote:
 On Thursday, 6 February 2014 at 05:27:22 UTC, Mike Parker wrote:
 A cursory look at Nitro suggests they are using an ECS with 
 DOP. Given that they're providing ranges for iterating the 
 data, it will fit into D's range-based component pipelines 
 (std.algorithm and such).
That seems cool. I can only imagine the possibilities... auto shipComponents = components.filter!(a => a.hasComponent!SpaceShip); foreach (i, spaceship; taskPool.parallel(shipComponents)) { spaceship.name = format("Ship %i", i); }
You're example almost works. With the EntityComponentManager API it works like this: auto shipEntities = ecm.query!SpaceShip(); foreach (i, spaceship; taskPool.parallel(shipEntities)) { auto shipData = spaceship.getComponent!SpaceShip(); shipData.name = format("Ship %i", i); } Now that unittests are written, documentation is on its way and should be ready in a few days.
Feb 06 2014
parent reply Mike Parker <aldacron gmail.com> writes:
On 2/7/2014 12:14 AM, Paul Freund wrote:
 You're example almost works. With the EntityComponentManager API it
 works like this:

          auto shipEntities = ecm.query!SpaceShip();

          foreach (i, spaceship; taskPool.parallel(shipEntities))
          {
              auto shipData = spaceship.getComponent!SpaceShip();
              shipData.name = format("Ship %i", i);
          }

 Now that unittests are written, documentation is on its way and should
 be ready in a few days.
Ah, so there's no data-orientation here. It's strictly entity-centric. Or do you have a way to iterate components independently of entities?
Feb 07 2014
parent reply "Zoadian" <github zoadian.de> writes:
On Friday, 7 February 2014 at 08:09:10 UTC, Mike Parker wrote:
 On 2/7/2014 12:14 AM, Paul Freund wrote:
 You're example almost works. With the EntityComponentManager 
 API it
 works like this:

         auto shipEntities = ecm.query!SpaceShip();

         foreach (i, spaceship; taskPool.parallel(shipEntities))
         {
             auto shipData = spaceship.getComponent!SpaceShip();
             shipData.name = format("Ship %i", i);
         }

 Now that unittests are written, documentation is on its way 
 and should
 be ready in a few days.
Ah, so there's no data-orientation here. It's strictly entity-centric. Or do you have a way to iterate components independently of entities?
There will be a way to iterate directly over components. Component fields will automatically be split into an structure of arrays. So let's assume we have: struct Point {int x; int y; int z; } Component struct Translation { int a; Point b; Point c; } Nitro will store Translation like this, so it is even possible to iterate over parts of components: Entity[] int[] for a int[] for b.x int[] for b.y int[] for b.z int[] for c.x int[] for c.y int[] for c.z Nitros getComponent functions will return Accessor!Translation, so if you do this, only int[] for b.x is accessed. foreach(e; ecs.query!Translation) { auto trans = e.getComponent!Translation(); trans.b.x = 0; }
Feb 07 2014
parent reply "Francesco Cattoglio" <francesco.cattoglio gmail.com> writes:
On Friday, 7 February 2014 at 09:47:43 UTC, Zoadian wrote:
 On Friday, 7 February 2014 at 08:09:10 UTC, Mike Parker wrote:
 Nitro will store Translation like this, so it is even possible 
 to iterate over parts of components:


   Entity[]
   int[] for a
   int[] for b.x
   int[] for b.y
   int[] for b.z
   int[] for c.x
   int[] for c.y
   int[] for c.z
This looks nice and everything, but won't it slow down access times quite a lot?
Feb 07 2014
parent Marco Leise <Marco.Leise gmx.de> writes:
Am Fri, 07 Feb 2014 09:58:52 +0000
schrieb "Francesco Cattoglio" <francesco.cattoglio gmail.com>:

 On Friday, 7 February 2014 at 09:47:43 UTC, Zoadian wrote:
 On Friday, 7 February 2014 at 08:09:10 UTC, Mike Parker wrote:
 Nitro will store Translation like this, so it is even possible 
 to iterate over parts of components:


   Entity[]
   int[] for a
   int[] for b.x
   int[] for b.y
   int[] for b.z
   int[] for c.x
   int[] for c.y
   int[] for c.z
This looks nice and everything, but won't it slow down access times quite a lot?
Yeah, looks like a point is now spread over 3 cache lines. As long as you access memory strictly forwards or backwards, x86 can at least stream the data from RAM in the background. Now if you check for collisions of 2 objects you'll need up to 6 spread out cache lines, where a compact layout would need one cache-line per object (2). The trouble is that RAM is not only much slower to access, but also it seems to take a while for the data from RAM to arrive in the CPU, making it important to know ahead of time where the next data has to be read from. The combined effect can mean a ~100 times slow down. So x86 got a sophisticated prefetcher, that can track several sequential memory reads (e.g. up to 16) either forwards or backwards through memory and loads those locations into CPU caches before they are needed. If you access pattern is seemingly random to the CPU it might in the worst case still try to prefetch, clogging the memory bandwidth and not achieving anything useful :p. -- Marco
Feb 07 2014
prev sibling parent reply "nikki" <nikkikoole gmail.com> writes:
I've written a coffeescript Entity Component System 
once(https://github.com/NikkiKoole/ces), I am planning on using 
https://github.com/elvisxzhou/artemisd for my D needs and I am 
hoping it's good (and hoping it does the components in some cache 
friendly manner).

how does your nitro compare?
Sep 04 2014
parent "nikki" <nikkikoole gmail.com> writes:
http://t-machine.org/index.php/2014/03/08/data-structures-for-entity-system
-contiguous-memory/ 
has some very nice insights in the memory issue.
Sep 04 2014
prev sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
Sorry for this belated reply, I have been rather busy with other
matters.


On Tue, Feb 04, 2014 at 04:12:48AM +0000, digitalmars-d-bounces puremagic.com
wrote:
 I loved reading Walter's component programming article in Dr. Dobb's
 [0] in late 2012. I had missed H. S. Teoh's mid 2013 article on
 calendar textual formatting using the component approach [1], but
 fortunately Ali brought it to my attention recently, and I also find
 it absolutely fascinating!
Thanks!
 I think what's most interesting with Teoh's article (and I think
 that was Ali's point when he mentioned the article to me) is that
 the calendar example is not as an obvious target for the component
 approach, or at least that the design and implementation is not as
 obvious for someone new to that approach.
 
 Now, while Teoh's example is much more complex than Walter's, both
 examples are for cases of pipelined problems (source -> filter1 ->
 filter2 -> sink). What I have been wondering during the last few
 days is how much this "component programming" approach could be
 applied to scenarios where you would normally have a jumble of
 objects. For instance, try to picture a game or simulation where
 spaceships fire at each other, pick up objects, communicate, and so
 on, or something like that. My instinct would be to code a solution
 which would be classified as typical OOP code. Would it be possible
 to come up with a solution that would be more in the spirit of
 "component programming"? Or are such solutions only
 practical/applicable for pipeline-like scenarios?
[...] I would say that while it's insightful to apply different paradigms to solve the same problem, one shouldn't make the mistake of shoehorning *everything* into the same approach. This is what Java does with OO, for example, to the detriment of every other paradigm, and frankly, after a while all those singleton classes with static methods just start to smell more and more like ways of working around the OO rather than with it. Having said that, though, the component approach is highly applicable, often in unexpected areas and unexpected ways, esp. when you couple it with D's range-based concept. There are certainly algorithms where it makes more sense to treat your data as a graph rather than a linear sequence of nodes, but it's also true that a good percentage of all code is just variations on linear processing, so pipelined component-style programming would definitely be applicable in many places. And nothing says you can't intermix component-style code with OO, or something else. One key insight is that sometimes you want to separate the object itself from a range over that object -- for example, I work with polytopes (higher-dimensional analogues of polygons and polyhedra), and it's useful to have, say, a range over all vertices, or a range over all edges, but it's also useful to separate these ranges from the polytope itself, which can be stored in a more compact form, or in a form that's more amenable to fast queries, e.g., find all faces that contain vertex X without needing to iterate over every face in the polytope (which you'd end up doing if you use filter() on the range of all faces). The query function can return a range over faces, so that it can be piped into other range-based functions for further processing. Thus, you can have a mix of different paradigms complementing each other. The other underlying theme in my article, which is also one of the key points of the Jackson Structured Programming that I alluded to, is the identification and separation of mismatching structures in order to simplify the code and eliminate code smells caused by ad hoc methods of structure conflict resolution (boolean flags are a common symptom of this malady). This isn't limited to pipelined programs, but applies in general. One could analyze OOP in this way, for example. OO lore says that objects should be cohesive and loosely-coupled -- we could say that cohesiveness means that the data stored in the object has corresponding structures, and loose coupling means that if an object's data has conflicting structures, it's time to consider splitting it into two different objects instead. T -- I've been around long enough to have seen an endless parade of magic new techniques du jour, most of which purport to remove the necessity of thought about your programming problem. In the end they wind up contributing one or two pieces to the collective wisdom, and fade away in the rearview mirror. -- Walter Bright
Feb 12 2014
next sibling parent reply "David Ledez" <david.ledez laposte.net> writes:
Dear T,

I've read with a great attention your reply regarding ECS and 
OOP. You mention that you work on high-dimensional polytope and a 
data structure able to support fast topological query with 
compact storage. I would be very interested in having a look to 
your data structure. Do you have any document about your design 
and the concepts? Any github?
This thred is really extremely instructive. I m looking forward 
to reading your reply.
Regards,

     - David -

On Wednesday, 12 February 2014 at 17:38:30 UTC, H. S. Teoh wrote:
 Sorry for this belated reply, I have been rather busy with other
 matters.


 On Tue, Feb 04, 2014 at 04:12:48AM +0000, 
 digitalmars-d-bounces puremagic.com wrote:
 I loved reading Walter's component programming article in Dr. 
 Dobb's
 [0] in late 2012. I had missed H. S. Teoh's mid 2013 article on
 calendar textual formatting using the component approach [1], 
 but
 fortunately Ali brought it to my attention recently, and I 
 also find
 it absolutely fascinating!
Thanks!
 I think what's most interesting with Teoh's article (and I 
 think
 that was Ali's point when he mentioned the article to me) is 
 that
 the calendar example is not as an obvious target for the 
 component
 approach, or at least that the design and implementation is 
 not as
 obvious for someone new to that approach.
 
 Now, while Teoh's example is much more complex than Walter's, 
 both
 examples are for cases of pipelined problems (source -> 
 filter1 ->
 filter2 -> sink). What I have been wondering during the last 
 few
 days is how much this "component programming" approach could be
 applied to scenarios where you would normally have a jumble of
 objects. For instance, try to picture a game or simulation 
 where
 spaceships fire at each other, pick up objects, communicate, 
 and so
 on, or something like that. My instinct would be to code a 
 solution
 which would be classified as typical OOP code. Would it be 
 possible
 to come up with a solution that would be more in the spirit of
 "component programming"? Or are such solutions only
 practical/applicable for pipeline-like scenarios?
[...] I would say that while it's insightful to apply different paradigms to solve the same problem, one shouldn't make the mistake of shoehorning *everything* into the same approach. This is what Java does with OO, for example, to the detriment of every other paradigm, and frankly, after a while all those singleton classes with static methods just start to smell more and more like ways of working around the OO rather than with it. Having said that, though, the component approach is highly applicable, often in unexpected areas and unexpected ways, esp. when you couple it with D's range-based concept. There are certainly algorithms where it makes more sense to treat your data as a graph rather than a linear sequence of nodes, but it's also true that a good percentage of all code is just variations on linear processing, so pipelined component-style programming would definitely be applicable in many places. And nothing says you can't intermix component-style code with OO, or something else. One key insight is that sometimes you want to separate the object itself from a range over that object -- for example, I work with polytopes (higher-dimensional analogues of polygons and polyhedra), and it's useful to have, say, a range over all vertices, or a range over all edges, but it's also useful to separate these ranges from the polytope itself, which can be stored in a more compact form, or in a form that's more amenable to fast queries, e.g., find all faces that contain vertex X without needing to iterate over every face in the polytope (which you'd end up doing if you use filter() on the range of all faces). The query function can return a range over faces, so that it can be piped into other range-based functions for further processing. Thus, you can have a mix of different paradigms complementing each other. The other underlying theme in my article, which is also one of the key points of the Jackson Structured Programming that I alluded to, is the identification and separation of mismatching structures in order to simplify the code and eliminate code smells caused by ad hoc methods of structure conflict resolution (boolean flags are a common symptom of this malady). This isn't limited to pipelined programs, but applies in general. One could analyze OOP in this way, for example. OO lore says that objects should be cohesive and loosely-coupled -- we could say that cohesiveness means that the data stored in the object has corresponding structures, and loose coupling means that if an object's data has conflicting structures, it's time to consider splitting it into two different objects instead. T
Sep 04 2014
parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
Sorry for the very late reply; my internet connection was down for 2
weeks and only came back yesterday.


On Thu, Sep 04, 2014 at 08:03:51AM +0000, David Ledez via Digitalmars-d wrote:
 Dear T,
 
 I've read with a great attention your reply regarding ECS and OOP. You
 mention that you work on high-dimensional polytope and a data
 structure able to support fast topological query with compact storage.
 I would be very interested in having a look to your data structure. Do
 you have any document about your design and the concepts? Any github?
I'm sorry to disappoint, but I was merely alluding to the fact that separating the range-based interface from the container allows one to freely choose the most suitable structure to represent the polytope, while using a proxy type to expose a range interface to allow generic algorithms to work with it. My code is currently still very much a work in progress, and I don't really have very much to show for it yet. The basic design, however, is along these lines: - A Polytope interface representing any abstract polytope-like structure in the program (see below for details); - A ConcretePolytope class (implementing a Polytope) that stores a face lattice, represented as a graph, the nodes of which are obtained by running a convex hull + lattice enumeration algorithm on the source data. - The Polytope interface has abstract methods for enumerating sub-polytopes (as input/forward ranges), which concrete classes like ConcretePolytope implement; the idea being that a face of a Polytope is also a Polytope, and while the underlying implementation may be different (e.g., the range of Polytopes representing the (n-1)-dimensional facets may have a different representation from, say, the 1-dimensional edges), they implement the same interface so generic algorithms can work with them without special-casing. This is, of course, just plain old OO, except perhaps for the range API part. The sub-polytope enumeration methods return proxy types that implement the range API, with Polytope as the element type, so you can use the usual std.algorithm / std.range tools on them -- .find, .filter, .chain, .cycle, .takeN, etc.. The proxy types are themselves interfaces, meaning that you can write a proxy Polytope implementation that performs some complex query over the lattice structure of ConcretePolytope, and still present a nice range API over its elements, and you can pass this to any of the usual range-based algorithms for searching, filtering, composing, etc., with no code change required in the algorithms. Again, this is just plain ole OO (Liskov substitution, in particular), seasoned with some D-flavored range-styled code. So you could, in theory ('cos I haven't actually implemented this part yet), construct some complex query over the polytope, and ask the program to enumerate, say, all 5D faces subject to some given filter, say they must have a certain number of 3D subfaces. In code, it could be as simple as: ConcretePolytope p = ...; auto query = new PolyQuery(...) // construct complex proxy Polytope from p // The query proxy object is still a Polytope assert(is(typeof(query) : Polytope)); query.faces(5) // get range of faces of dimension 5 .filter!((p) => p.faces(3).count > 10) // filter by faces with more than 10 3D faces .copy((p) { // print a list of face ID's in the result writefln("%s", p.id); }); Notice how the second block of code can actually apply directly to ConcretePolytope (since it implements the Polytope interface), or to a PolyQuery object (which represents some subset of the ConcretePolytope's surface), or to anything, really, that implements the Polytope interface -- perhaps a specific face of a ConcretePolytope. Due to the range API implemented by the .faces method, you can then process this range downstream using all of the usual range-based algorithms. Also note that by abstracting the implementation details of ConcretePolytope away, it is also possible to implement a different kind of concrete polytope, say one where the full face lattice isn't computed beforehand, like I'm doing now, but which is lazily computed depending on what kind of queries you run on it -- i.e., if you call faces(6), then it will lazily construct the 6-dimensional faces of the polytope on the fly as you iterate over the range, and if you iterate over, say, the 5-dimensional faces of a subset of the 6D faces, then the lattice enumeration algorithm will only be run for those subset of faces, rather than the entire polytope. As you probably know, the face lattice of a general polytope can be exponential in the size of its initial vertex/hyperplane representation, so allowing this kind of lazy evaluation *without needing to change any downstream code* is a big advantage. It is also possible to implement a polytope whose faces are mathematically-generated, allowing compact storage. For example, you could generate it from a symmetry group, which alleviates the need to store all vertices / hyperplanes (you can generate everything from the vertex figures, for example). Again, none of the downstream code needs to change; they will Just Work(tm). This isn't anything radically new, really, it's just basic OO design, but just with a little range-based spice added to it. :) T -- They say that "guns don't kill people, people kill people." Well I think the gun helps. If you just stood there and yelled BANG, I don't think you'd kill too many people. -- Eddie Izzard, Dressed to Kill
Sep 10 2014
prev sibling parent reply "Chris" <wendlec tcd.ie> writes:
On Wednesday, 12 February 2014 at 17:38:30 UTC, H. S. Teoh wrote:

 I would say that while it's insightful to apply different 
 paradigms to
 solve the same problem, one shouldn't make the mistake of 
 shoehorning
 *everything* into the same approach. This is what Java does 
 with OO, for
 example, to the detriment of every other paradigm, and frankly, 
 after a
 while all those singleton classes with static methods just 
 start to
 smell more and more like ways of working around the OO rather 
 than with
 it.
I found myself using singelton classes more and more until I decided it was time to drop a strict OO approach.
 Having said that, though, the component approach is highly 
 applicable,
 often in unexpected areas and unexpected ways, esp. when you 
 couple it
 with D's range-based concept. There are certainly algorithms 
 where it
 makes more sense to treat your data as a graph rather than a 
 linear
 sequence of nodes, but it's also true that a good percentage of 
 all code
 is just variations on linear processing, so pipelined 
 component-style
 programming would definitely be applicable in many places.

 And nothing says you can't intermix component-style code with 
 OO, or
 something else.
That's what I've been doing for the last 1 1/2 years. I use classes where it makes _sense_, not as the ruling paradigm, then add structs (components), ranges and templates. The good thing about the freedom D offers is that it encourages you to think about the fundamental logic of your program and use tailor made solutions for a given problem - instead of a one size fits all approach that is bound to lead you down a cul de sac. In a way D has given the power back to the programmer's brain.
 One key insight is that sometimes you want to separate
 the object itself from a range over that object -- for example, 
 I work
 with polytopes (higher-dimensional analogues of polygons and 
 polyhedra),
 and it's useful to have, say, a range over all vertices, or a 
 range over
 all edges, but it's also useful to separate these ranges from 
 the
 polytope itself, which can be stored in a more compact form, or 
 in a
 form that's more amenable to fast queries, e.g., find all faces 
 that
 contain vertex X without needing to iterate over every face in 
 the
 polytope (which you'd end up doing if you use filter() on the 
 range of
 all faces). The query function can return a range over faces, 
 so that it
 can be piped into other range-based functions for further 
 processing.
 Thus, you can have a mix of different paradigms complementing 
 each
 other.

 The other underlying theme in my article, which is also one of 
 the key
 points of the Jackson Structured Programming that I alluded to, 
 is the
 identification and separation of mismatching structures in 
 order to
 simplify the code and eliminate code smells caused by ad hoc 
 methods of
 structure conflict resolution (boolean flags are a common 
 symptom of
 this malady). This isn't limited to pipelined programs, but 
 applies in
 general. One could analyze OOP in this way, for example. OO 
 lore says
 that objects should be cohesive and loosely-coupled -- we could 
 say that
 cohesiveness means that the data stored in the object has 
 corresponding
 structures, and loose coupling means that if an object's data 
 has
 conflicting structures, it's time to consider splitting it into 
 two
 different objects instead.


 T
Sep 04 2014
parent "Paulo Pinto" <pjmlp progtools.org> writes:
On Thursday, 4 September 2014 at 10:12:13 UTC, Chris wrote:
 On Wednesday, 12 February 2014 at 17:38:30 UTC, H. S. Teoh 
 wrote:

 I would say that while it's insightful to apply different 
 paradigms to
 solve the same problem, one shouldn't make the mistake of 
 shoehorning
 *everything* into the same approach. This is what Java does 
 with OO, for
 example, to the detriment of every other paradigm, and 
 frankly, after a
 while all those singleton classes with static methods just 
 start to
 smell more and more like ways of working around the OO rather 
 than with
 it.
I found myself using singelton classes more and more until I decided it was time to drop a strict OO approach.
In Smalltalk and Eiffel, where everything is an object this tends not to be a problem, because they lack modules anyway. So objects with class methods play the role of modules in a way package support, it would be more natural to associate certain code with the package, not with objects. be encapsulated into a (dummy) class, for example.
 Having said that, though, the component approach is highly 
 applicable,
 often in unexpected areas and unexpected ways, esp. when you 
 couple it
 with D's range-based concept. There are certainly algorithms 
 where it
 makes more sense to treat your data as a graph rather than a 
 linear
 sequence of nodes, but it's also true that a good percentage 
 of all code
 is just variations on linear processing, so pipelined 
 component-style
 programming would definitely be applicable in many places.

 And nothing says you can't intermix component-style code with 
 OO, or
 something else.
That's what I've been doing for the last 1 1/2 years. I use classes where it makes _sense_, not as the ruling paradigm, then add structs (components), ranges and templates. The good thing about the freedom D offers is that it encourages you to think about the fundamental logic of your program and use tailor made solutions for a given problem - instead of a one size fits all approach that is bound to lead you down a cul de sac. In a way D has given the power back to the programmer's brain.
This was actually my first issue when I learned OO in Turbo Pascal 5.5, my first OO language. It was a learning process to understand what should become an object, and what should stay as usual procedural (functions/records/units) code. Regarding component programming in general, this used to be a good book on the subject. Only read the first edition (based in Component Pascal), the second was updated to more "modern" languages. http://www.amazon.com/Component-Software-Object-Oriented-Programming-Addison-Wesley/dp/032175302X -- Paulo
Sep 04 2014