February 23, 2003
Walter wrote:
> "Bill Cox" <bill@viasic.com> wrote in message
> news:3E584E7A.2060908@viasic.com...
> 
>>If I read it right, "virtual classes" are simply classes that can be
>>replaced (just like virtual functions).  You define a higher level
>>containing class, inherit the whole thing, and replace classes that need
>>custom definitions for your app.
> 
> 
> That's the way I read it, too, but how is it different from (in C++):
> 
>     class Foo
>     {
>         class Bar *p;
>     };
> 
> where you can inherit from Foo, derive from Bar, and replace p?

I can try to use both techniques to inherit graph functionality into a hyper-graph, and let you know how I did.  I find a concrete example can help solidify things.  It might take me a couple of days to get around to it.

Bill

February 23, 2003

Walter wrote:
> "Bill Cox" <bill@viasic.com> wrote in message
> news:3E584E7A.2060908@viasic.com...
> 
>>If I read it right, "virtual classes" are simply classes that can be
>>replaced (just like virtual functions).  You define a higher level
>>containing class, inherit the whole thing, and replace classes that need
>>custom definitions for your app.
> 
> 
> That's the way I read it, too, but how is it different from (in C++):
> 
>     class Foo
>     {
>         class Bar *p;
>     };
> 
> where you can inherit from Foo, derive from Bar, and replace p?

I'll try out both methods and post how it goes.  It will probably take a couple days to get to it.

Bill

February 23, 2003
In article <b3a0o8$2sbt$1@digitaldaemon.com>, Walter says...
>That's the way I read it, too, but how is it different from (in C++):
>
>    class Foo
>    {
>        class Bar *p;
>    };
>
>where you can inherit from Foo, derive from Bar, and replace p?
>

Because now in your class inherited from Foo, you can only refer to p as a Bar (not the inherited type) unless you litter your code with type-casts. This can be a major source of type incompabilities and bugs.

Eiffel prominently features a fix for this conceptual problem; they call it "covariation". When you inherit from Foo, you can declare p as a member with a new type, but only if the type is Bar or a subclass of Bar.  It will then completely replace Foo's p.

I'm not sure if you could do this in D. It seems that you would run into problems in Foo's methods, for example if Foo's constructor is called on a subclass that replaces p, it might attempt to allocate a Bar instead of the appropriate subclass of Bar.

You can fix this incurring some performance lossage by using the properties methods in D or setter/getters in C++: instead of referring to p directly set and get p through a method that subclasses of Foo will override. That is really the idea lurking behind Sweeney's "virtual classes" concept.

I do agree that this problem can be a major constraint on large object-centric programs.

Dan


February 24, 2003
On Sat, 22 Feb 2003 05:51:03 +0000 (UTC), Mark Evans <Mark_member@pathlink.com> wrote:

> A must read from Paul Graham.  I could not have offered a better rationale for
> first-class functions, though the principle stated is general, and so deserves
> its own thread.

A tangental idea comes from Edward De Bono's call for Simplicity. Or put another way, a programming language should be simple but not simplistic, plus that in order to implement something that is simple to use, may very well involve a deal of complexity 'under the hood'.

-- 
xyzzy
February 24, 2003
"Bill Cox" <bill@viasic.com> wrote in message news:3E58454F.7030500@viasic.com...
> Oops... Sorry Patrick.  Ken suggested the pizza link.

I was trying to think back and remember mentioning anything about a framework to you!

I almost convinced myself that I might have, since I've done something similar with a code generator I wrote at work.

:-)


Ken Carpenter


February 24, 2003
"Mark T" <Mark_member@pathlink.com> wrote in message news:b3aonm$dpc$1@digitaldaemon.com...
> In article <b38jlm$1nb9$1@digitaldaemon.com>, Walter says...
> >
> >
> >"Mark Evans" <Mark_member@pathlink.com> wrote in message news:b37347$cdr$1@digitaldaemon.com...
> >> http://www.paulgraham.com/fix.html
> >
> >D: C++ is too complicated
> ; Java, C# use VMs and don't have generics

Many people hear "C# uses a VM" and assume that it's the same as Java.

Just to clarify, C# and other .NET languages are first compiled to a "VM" code (CIL), but they are then compiled to native code before they are executed.


Ken Carpenter


February 24, 2003
Thanks, your explanation makes sense to me. -Walter

"Dan Liebgold" <Dan_member@pathlink.com> wrote in message news:b3blva$17h2$1@digitaldaemon.com...
> In article <b3a0o8$2sbt$1@digitaldaemon.com>, Walter says...
> >That's the way I read it, too, but how is it different from (in C++):
> >
> >    class Foo
> >    {
> >        class Bar *p;
> >    };
> >
> >where you can inherit from Foo, derive from Bar, and replace p?
> >
>
> Because now in your class inherited from Foo, you can only refer to p as a
Bar
> (not the inherited type) unless you litter your code with type-casts. This
can
> be a major source of type incompabilities and bugs.
>
> Eiffel prominently features a fix for this conceptual problem; they call
it
> "covariation". When you inherit from Foo, you can declare p as a member
with a
> new type, but only if the type is Bar or a subclass of Bar.  It will then completely replace Foo's p.
>
> I'm not sure if you could do this in D. It seems that you would run into problems in Foo's methods, for example if Foo's constructor is called on a subclass that replaces p, it might attempt to allocate a Bar instead of
the
> appropriate subclass of Bar.
>
> You can fix this incurring some performance lossage by using the
properties
> methods in D or setter/getters in C++: instead of referring to p directly
set
> and get p through a method that subclasses of Foo will override. That is
really
> the idea lurking behind Sweeney's "virtual classes" concept.
>
> I do agree that this problem can be a major constraint on large
object-centric
> programs.
>
> Dan
>
>


February 24, 2003
> > >
> > >D: C++ is too complicated
> > ; Java, C# use VMs and don't have generics
>
> Many people hear "C# uses a VM" and assume that it's the same as Java.
>
> Just to clarify, C# and other .NET languages are first compiled to a "VM" code (CIL), but they are then compiled to native code before they are executed.
>
as do most modern JavaVM's (only the early 1.0.x where pure interpreters)
Java VM's are not all JIT (compile before) some are dynamic compilers
(compile if its used more than once as compilation + execute time can be
longer than interpret once time) also this allows the true main line of the
code to be compiled.
with C compilers 'if' is usally compiled as branch over the if clause, with
a dynamic compiler if the if cluase is not entered then it is compiled as a
branch out of the main line to the if clause making the common case code
faster (missed branches are less expencive on most cpu's than taken
branches).



February 24, 2003

Bill Cox wrote:
> Walter wrote:
> 
>> "Bill Cox" <bill@viasic.com> wrote in message
>> news:3E584E7A.2060908@viasic.com...
>>
>>> If I read it right, "virtual classes" are simply classes that can be
>>> replaced (just like virtual functions).  You define a higher level
>>> containing class, inherit the whole thing, and replace classes that need
>>> custom definitions for your app.
>>
>>
>>
>> That's the way I read it, too, but how is it different from (in C++):
>>
>>     class Foo
>>     {
>>         class Bar *p;
>>     };
>>
>> where you can inherit from Foo, derive from Bar, and replace p?
> 
> 
> I can try to use both techniques to inherit graph functionality into a hyper-graph, and let you know how I did.  I find a concrete example can help solidify things.  It might take me a couple of days to get around to it.
> 
> Bill
> 

I've messed around with reusing a simple graph package a bit, and now have a simple example.  The following code is from my toy compiler I've been playing with (which I call "Sail").  The 'reuse' construct is the same as Sather's "include" construct.  It's also similar to the "framework" Patrick Down proposed, and "virtual classes", and Eiffel's "covariation".  I like it because it puts the code needed to reuse the graph package to one place.  My second favorite is Patrick's frameworks, which read very nicely.

Here's a simple piece of graph code in Sail.  It parses, but I haven't implemented the reuse stuff yet, so it doesn't run.  The code just detects loops in directed graphs:

module graph;

class Graph {
    // We have a linked list of nodes, and nodes point back
    // to their graph.
    cascade LinkedList of Node nodes:graph;

    bool findLoop()
    {
	Node node;

        clearNodeFlags();
        // This itterator gets generated by the LinkedList code
	foreach(nodes, node) {
	    if(!node.visited && node.findLoop()) {
		return true;
	    }
	}
	return false;
    }

    clearNodeFlags()
    {
	Node node;

	foreach(nodes, node) {
	    node.visited = false;
	    node.marked = false;
	}
    }
}

class Node {
    // This adds two linked-list containters to Node, and two back
    // pointers to Node from Edge.
    cascade LinkedList of Edge outEdges:fromNode, inEdges:toNode;
    bool visited, marked;

    findLoop() {
	Node otherNode;
	Edge edge;

        visited = true;
	marked = true;
	foreach(outEdges, edge) {
            // The fromNode.owner member of Edge is generated by
            // the linked-list outEdges relationship.
	    otherNode = edge.fromNode.owner;
	    if(otherNode.marked || !otherNode.visited &&
                    otherNode.findLoop()) {
	        marked = false;
		return true;
	    }
	}
	marked = false;
	return false;
    }
}

class Edge {
    // No fields are declared here, but the Node back-pointers are added
    // by the linked-list containers in Node.
}

Now lets reuse the code in a realistic application, a simple PCB router.  A printed circuit board has layers of wires, interconnected by vias. Net's are a collection of these wires, which interconnect components on the board.  I just list the Net, Wire, and Via classes, but their would be others to model the whole board.

module router;

class Net {
    // We have a doubly linked list of wires, and wires point back
    // to their nets.
    cascade DoublyLinkedList of Wire wires:net;
}

class Wire {
    // We have an undirected graph of wires connected by vias.
    // This still requires two containers, abitrarily called
    // via1s and via2s.
    cascade LinkedList of Via via1s:wire1, via2s:wire2;
}

class Via {
}


Note that this sub-schema of classes is isomorphic to the schema for directed graphs.  (Schema just means a diagram of classes drawn as nodes, and relationships drawn as edges.  Schemas are isomorphic if they are basically the same graph, just with names changed.)  To reuse all the graph stuff, I need only specify the mapping of classes and relationships between the modules:

reuse graph {
    // Nets act like Graphs
    Graph : Net;
    // The wires relationship acts like the nodes relationship
    Graph.nodes : Net.wires;
    Node : Route;
    Node.outEdges : Route.via1s;
    Node.inEdges : Route.via2s;
    Edge : Via;
}

Now, I can determine if a Net has a loop with net.findLoop().  Note that I could include graph functionality multiple times if needed, since there may be multiple isomorphisms with sub-schemas of my module.

This code causes all the graph module's code to be included in the router module.  During the copy, we replace or rename identifiers as specified in the mapping.  Unmapped identifiers are copied as-is.

Without a construct of this kind, we are left with templates, interfaces, and inheritance to try to reuse the graph package.  So far as I can tell, none of these work well for this kind of code reuse.

Patrick's "frameworks" work perfectly well here.  Frameworks look more like an inheritance mechanism, while this "reuse" construct looks more like a fancy preprocessor thing where we include code, but replace some things.  Sather calls it "include", but is otherwise identical to this "reuse" construct.  I think "include" would possibly confuse C programmers, but perhaps it would give them the right notion.

Bill

February 24, 2003
The reuse construct should have read:

reuse graph {
    // Nets act like Graphs
    Graph : Net;
    // The wires relationship acts like the nodes relationship
    Graph.nodes : Net.wires;
    Node : Wire;
    Node.outEdges : Wire.via1s;
    Node.inEdges : Wire.via2s;
    Edge : Via;
}

That's the problem with untested code... It's never right.

I just work on this thing for fun, but I hope to eventually implement full support for the reuse construct.  If it would help, I could do it sooner than later, and post what kind of trouble I run into.

Bill