220 likes | 228 Views
Family Polymorphism. by Erik Ernst. Outline. Class families The “ OnOffGraph ” example Traditional polymorphism C++ approach Family polymorphism Implementation difficulties Mutable instances Where to find the pointer When is it type safe Comparison with VirtualTypes. Class Families.
E N D
Family Polymorphism by Erik Ernst
Outline • Class families • The “OnOffGraph” example • Traditional polymorphism • C++ approach • Family polymorphism • Implementation difficulties • Mutable instances • Where to find the pointer • When is it type safe • Comparison with VirtualTypes
Class Families • Express multi-object relations • Flexibility • Safety • Classes are declared and managed in a “family” and they polymorphic together.
Node Edge Families OnOffNode OnOffEdge The OnOffGraph Example • There are edges and nodes in a graph. • I.e. Edge and Node is in a family Graph. • OnOffEdge and OnOffNode are in the family OnOffGraph.
Traditional Polymorphism(The Naïve approach) class Node{ boolean touches(Edge e){… } }class Edge{ Node n1, n2; } class OnOffNode extends Node{ boolean touches(Edge e){ return ((OnOffEdge)e).enabled ? super.touches(e) : false; }}class OnOffEdge extends Edge{ boolean enabled; } void build(Node n, Edge e){ e.n1 = e.n2 = n; if(n.touches(e)) println(“OK”);}
Traditional Polymorphism (cont.) • build(new Node(), new Edge()) • build(new OnOffNode(), new OnOffEdge()) • build(new OnOffNode(), new Edge())// ClassCastException • build(new Node(), new OnOffNode())// “works” • Reuse: Yes; Safety: No
template<class N, class E> struct EdgeF{ N* n1, n2; };template<class N, class E> struct NodeF{ virtual bool touches(E* e); }; struct Edge: public EdgeF<Node, Edge>{};struct Node: public NodeF<Node, Edge>{}; template<class ON, class OE> struct OnOffEdgeF: public EdgeF<ON, OE>{ bool enabled; };template<class ON, class OE> strut OnOffNodeF: public NodeF<ON, OE>{ bool touches(OE* e){ return e->enabled ? NodeF<ON, OE>::touches(e) : false; }}; struct OnOffEdge: public OnOffEdgeF<OnOffNode, OnOffEdge>{};struct OnOffNode: public OnOffNodeF<OnOffNode, OnOffEdge>{}; void build1(Node* n, Edge* e){ e->n1 = e->n2 = n; if(n->touches(e)) cout << “OK\n”;} void build2(OnOffNode* n, OnOffEdge* e){ e->n1 = e->n2 = n; if(n->touches(e)) cout << “OK\n”;} C++ approach
C++ approach (cont.) • build1(new Node, new Edge) • build2(new OnOffNode, new OnOffEdge) • build1(new OnOffNode, new Edge)// type error • build2(new Node, new OnOffEdge)// type error • Safety: Yes; Reuse: No
C++ approach problems • Reusability: using template (but not really “reuse” the function) template<class N, class E> void build(N* n, E* e){ … } • But problems arise
template<class N, class E>struct GraphF{ typedef N Node; typedef E Edge;}; typedef GraphF<Node, Edge> Graph;typedef GraphF<OnOffNode, OnOffEdge> OnOffGraph; template<class G>void build(const G&, typename G::Node* n, typename G::Edge* e){ … } build(Graph(), new Node, new Edge); C++ approach problems (cont.) • Every involved function have to be template functions and the exact type (Node and Edge, or OnOffNode and OnOffEdge) should be known, not any thing like “a subfamily of Graph.”
C++ approach problems (cont.) • A template function may not be a virtual member function • Limits usage on known Node-Edge system • Generic in Java 1.5 doesn’t have such problem
C++ approach problems (cont.) • No coupled containers to store nodes and edges belonging together template<class G>struct NodesAndEdges{ vector<typename G::Node> nodes; vector<typename G::Edge> edges;}
C++ approach problems (cont.) • All usages of nodes and edges are performed in a context where the exact classes are known • May increase the dependency of implementation details
Family polymorphism (# Graph: (# Node:< (# touches:< (# e: ^Edge; b: @boolean enter e[] do (this(Node)=e.n1) or (this(Node)=e.n2) -> b exit b #) exit this(Node)[] #); Edge:< (# n1, n2: ^Node exit this(Edge)[] #) #); OnOffGraph: Graph (# Node::<(# touches::<!(# do (if e.enabled then INNER if) #)#) Edge::<(# enabled: @boolean #) #); build: (# g:< @Graph; n: ^g.Node; e: ^g.Edge enter(n[], e[]) do n -> e.n1[] -> e.n2[]; (if (e->n.touches) then ‘OK’ ->putline if) #);
Family polymorphism (cont.) • g1: @Graph; g2: @OnOffGraph • (g1.Node, g1.Edge) -> build(# g::@g1 #) • (g2.Node, g2.Edge) -> build(# g::@g2 #) • (g2.Node, g1.Edge) -> build(# g::@g1 #)(* type error *) • (g2.Node, g1.Edge) -> build(# g::@g2 #)(* type error *) • Reuse: Yes; Safety: Yes • Class families are associated with instances (g1, g2), not classes (Graph, OnOffGraph)
Family polymorphism (cont.) • Coupled containers: NodesAndEdges:(# g:< @Graph; nodes: @list(# element::g.Node #); edges: @list(# element::g.Edge #)#) myGraph: @LabelledGraph;myNodesAndEdges: @NodesAndEdges(# g::@myGraph #) listBuild:(# ne:< @NodesAndEdges; n: ne.g.Node; e: ^ne.g.Edge do ne.nodes.head -> n[]; ne.edges.head -> e[]; (n, e) -> build(# g::@ne.g #)#) m1: (# x: ^NodesAndEdges enter x[] do listBuild(# ne::@x #) #)m2: (# x: ^NodesAndEdges enter x[] do x[] -> m1 #)
Implementation DifficultiesMutable Instances • Mutable instance “ne” in listBuild will change the type at runtime and destroy the correctness. • Only the types from the immutable references are considered the same in multiple appearances
Implementation DifficultiesWhere Is The Pointer • Circle and Rectangle and subclasses of GraphicalObjects with method draw • Traverse a List whose elements are typed as GraphicalObjects and invoke draw of them. • There may not exist any pointers typed by the actual class to each object in the list in the program state
Implementation DifficultiesWhere Is The Pointer (cont.) • To type the families correctly, the single-object way does not work • Possible approach: use wrapper classes like NodesAndEdges which stores a Graph instance then have a list of these wrappers • Rediscover: instanceof • Restrict usage on known families
Implementation DifficultiesWhen is it type safe • Virtual patterns (virtual types) are attributes of objects, not classes • Virtual patterns have a kind of existential type:x is an object of type T (typed as T)V is a virtual attribute in T x.V has the type T.V i.e. ∃T’V ≤ TV . T’V • y is another instance typed as Ty.V can be ∃T’’V ≤ TV . T’’VT’’V may not equal to T’V
Implementation DifficultiesWhen is it type safe (cont.) • Virtual types can be change into ordinary types by final binding[Tor98] • Immutable reference is equally valid • this is an immutable reference
Comparison with VirtualTypes • [Bru98] • Based on exact references • Virtual types are attributes of classes • [Tor98] • Type system similar • The language in it is a pseudo one • Not emphasized that virtual types are attributes of objects