Add a covariant `copy` method on `Graph` (#109)

Summary:
Clients of `Graph` that wish to treat the graph as immutable will
benefit from a `copy` method. We should provide it on `Graph` instead of
asking clients to reimplement it because it affords us the opportunity
to get the type signature right: in particular, copying should allow
upcasting of the type parameters, even though `Graph` itself is
invariant.

Paired with @dandelionmane.

Test Plan:
Unit tests added. Run `yarn flow` and `yarn test`. To check that
downcasting is not allowed, change the types in the new static test case
in `graph.test.js` to be contravariant instead of covariant, and note
that `yarn flow` fails.

wchargin-branch: graph-copy
This commit is contained in:
William Chargin 2018-03-26 13:58:12 -07:00 committed by Dandelion Mané
parent 007cf88172
commit 26508051a4
2 changed files with 31 additions and 0 deletions

View File

@ -41,6 +41,10 @@ export class Graph<NP, EP> {
this._inEdges = new AddressMap(); this._inEdges = new AddressMap();
} }
copy(): Graph<$Supertype<NP>, $Supertype<EP>> {
return Graph.mergeConservative(new Graph(), this);
}
equals(that: Graph<NP, EP>): boolean { equals(that: Graph<NP, EP>): boolean {
return this._nodes.equals(that._nodes) && this._edges.equals(that._edges); return this._nodes.equals(that._nodes) && this._edges.equals(that._edges);
} }

View File

@ -631,5 +631,32 @@ describe("graph", () => {
}); });
}); });
}); });
describe("copy", () => {
it("separates references from the original", () => {
const g1 = demoData.advancedMealGraph();
const g2 = g1.copy();
const newNode = () => ({
address: demoData.makeAddress("brand-new"),
payload: 777,
});
g2.addNode(newNode());
expect(g1.getNode(newNode().address)).toBeUndefined();
expect(g2.getNode(newNode().address)).toEqual(newNode());
});
it("yields a result equal to the original", () => {
const g1 = demoData.advancedMealGraph();
const g2 = g1.copy();
expect(g1.equals(g2)).toBe(true);
expect(g1.equals(demoData.advancedMealGraph())).toBe(true);
});
function itAllowsUpcastingPayloadTypes(
g: Graph<{x: string, y: number}, boolean>
): Graph<{x: string}, ?boolean> {
return g.copy();
}
});
}); });
}); });