Hi,
There is a question regarding usage of functional programming techiques in C# code. Example
Let we have interface
interface IGraph { /*contains vertices and edges*/}
Suppose we need to layout graph's vertices (assign Point to each vertex).
interface ILayoutInfo {
Point GetVertexPoint(vertex);
}
Simple layout route can have such signature:
ILayoutInfo SimpleLayout(IGraph graph);
Which can be used in such way
void Demo() {
IGraph graph = CreateGraphInAnyWay();
ILayoutInfo layout = SimpleLayout(graph);
PrintCoordinates(graph,layout);
}
In design below PrintCoordinates need both references to graph and layout.
Consider functional style design where layouting routing augments graph information with information about graph vertices coordenates.
ILayoutedGraph SimpleLayoutNew(IGraph graph);
Where ILayoutedGraph implements BOTH IGraph and ILayoutInfo
void DemoNew() {
IGraph graph = CreateGraphInAnyWay();
ILayoutedGraph layoutedGraph = SimpleLayoutNew(graph);
PrintCoordinatesNew(layoutedGraph);
}
1)In this design PrintCoordinatesNew gets only ONE parameter. 2)Weird interface ILayoutedGraph was born which doesn't contain any methods and just wraps other interfaces. If some library has other types like INetwork, ITree we end up creating wrap interfaces ILayoutedNetwork, ILayoutedTree (which is bad).
So such techique is used only in functinal languages just because they can't work in other way (there are no state so function must combine input with calculated info to be used by outer routines) or it is viable in imperative world too?
Thanks a lot,
PS: a more verbose pretty printed example can be found here http://tivadj.blogspot.com/2009/02/designing-c-api-in-functional-style.html
-
Can you implement this using a class that implements both interfaces and casting the returns as that class?
tivadj : The problem is what API (interfaces) to publish: traitional style case1) IGraph, ILayoutInfo and SimpleLayout(IGraph):ILayoutInfo or functional style case2) IGraph, ILayoutedGraph SimpleLayoutNew(IGraph):ILayoutedGraph ==== ILayoutedGraph really can be implemented by some internal class. -
There is no problem with your original API of
void Demo() { IGraph graph = CreateGraphInAnyWay(); ILayoutInfo layout = SimpleLayout(graph); PrintCoordinates(graph,layout); }It is perfectly functional. An imperative API would involve a change to
graphonce it is created. Egvoid Demo() { IGraph graph = CreateGraphInAnyWay(); graph.Layout(new SimpleLayout()); // changes the state of graph PrintCoordinates(graph); }As for the problem of ILayoutedGraph, ILayoutedTree, ILayoutedQueue, etc, I think you would get around this by generic classes in a functional languages and multiple inheritance in the OO languages where this is allowed.
Personally, I would recommend generics:
ILayout<a>whereais something with edges that needs to be laid out. -
If seems that what disturbs you when passing two arguments is that your layout (ILayoutInfo) is linked to the graph used to generate it. I would be meaningless to pass a layout with a graph that was not used to generate it.
In this case, you can keep a reference to the graph in the layout info, and provide an accessor on the ILayoutInfo interface.
This way, you can only pass the ILayoutInfo instance, and you PrintCoordinates function can still acces the graph that was used to generate the ILayoutInfo.
If you have other kind of object that can generate layout infos, use a common interface for them, or make the ILayoutInfo generic.
tivadj : Thanks. You really give me a breath of fresh air.
0 comments:
Post a Comment