Component programming in D programming language
By Walter bright
We have been trying to write reusable software. The author has 35 years of programming experience, but there is little reusable software. Copy / paste is not enough. I may miss it somewhere, and other programmers often feel the same way. It is not a lack of practice, nor is it better than in the past. We should have an in-depth understanding
Problematic samples: the abstraction has loopholes, relies on other codes, and the components are too specific (applicable to A, but not applicable to B). Go back to the problem:
What is a component?
● not only reusable software, there are many reusable code bases of non real components
● components have predefined interfaces. In this way, they can be exchanged, added and combined even if they are developed separately
● most libraries expose interfaces, but connect to other libraries, requiring assistants
So,
What is a common component interface?
● read input
● process input
● write output
Even if the program is not, the subsystem applies the model, and the pseudo code indicates:
source => algorithm => Remit
Or so:
source => 1 algorithm => 2 algorithm => Remit
I've seen it on the file and filter command line model of joint operation
● very successful and strong
● according to docking C of "document interface"
● documents are both source and sink
● algorithm is "filter"
● connect them with pipes
● even the advantage of pseudo file system
shortcoming
● evolution rather than design
● treat data as byte stream
● not applicable to random access algorithm
● but it shows what the components are and how they are distributed
Look at my code
void main(string[] args) { string pattern = args[1]; while (!feof(stdin)) { string line = getLine(stdin); if (match(pattern, line)) writeLine(stdout, line); } }
Unlike source = > ALGORITHM = > sink, it enters the vortex instead of the assembly plant. This is better:
void main(string[] args) { string pattern = args[1]; stdin => byLines => match(pattern) => stdout; //Input, by line, match, output }
Next design
● C + + is object-oriented and does not lead to better component programming
● C++ iostreams starts to look like source = > ALGORITHM = > sink by overloading the > > operator, but it is still a read / write file
● many successful C + + libraries, but they are not the components discussed here
● revolutionary C + + iterators and algorithms, STL Standard Template Library: not only files, algorithms, general interfaces, compiled into efficient code:
for (i = L.begin(); i != L.end(); ++i) ... use(*i)work ...
Still like a loop, then std::for_each(),std::transform(), but cannot be combined because iterators appear in pairs
Back to the drawing board
● source: stream, container, generator
● algorithm: filtering, mapping, reduction, sorting
● sink: flow, container
source | algorithm | Remit |
---|---|---|
file | sort | file |
tree | filter | tree |
array | map | array |
socket | reduce | socket |
list | max | list |
iota | search | |
random number | Odd number |
Summary requirements
● composability
● support powerful packaging
● generate industrial quality and efficiency code
● natural syntax: source = > ALGORITHM = > sink
● can be used with unknown type
Input interval requirements
● is data available? bool is null;
● read current input data: E front
● step to the next data: void popFront()
Input interval non type
● it is a concept
● three primitives are required: empty, front and popfront
Read from stdin:
private import core.stdc.stdio; struct StdinByChar {//Three primitives @property bool empty() { if (hasChar return false; auto c = fgetc(stdin); if (c == EOF return true; ch = cast(char)c; hasChar = true; return false; } @property char front() { return ch; } void popFront() { hasChar = false; } private: char ch; bool hasChar; }
Read from stdin and write to stdout:
for (auto r = StdinByChar(); !r.empty; r.popFront( {//Pay attention to primitives auto c = r.front; //here fputc(c, stdout); }
Add some language magic:
foreach (c; StdinByChar()) fputc(c, stdout); //Note that there are no types here
Forward Interval
Added a save attribute:
@property R save; (R Forward interval type)
Return the location copy, not the data. Both the original and copy can traverse the interval independently. The single linked list is a typical example. The forward interval is used for merging and sorting
Bidirectional interval
Added attributes and methods:
@property E back; void popBack();
Similar to front and popFront, but working in reverse. Double linked list is typical, and UTF-8 and UTF-16 are also bidirectional coding
Random access interval
add:
E opIndex(size_t I);
Index data with [] and add 2 options:
1. The @ property size of bidirectionalrange_ t length; Length attribute, which can be infinite
2. Infinite forwardrange. For infinite interval, empty is always false
Sink (output interval)
There are two put methods:
void put(E e);
Put e into the interval of type E
Output interval write standard output
struct StdoutByChar { void put(char c) { if (fputc(c, stdout) == EOF) throw new Exception("Mark errors"); } }
Recall earlier:
foreach (c; StdinByChar()) fputc(c, stdout);
Use the output interval to become:
StdoutByChar r; foreach (c; StdinByChar()) r.put(c);
Even handle errors correctly! It is copied from input to output. Copy:
void copy(ref StdinByChar source, ref StdoutByChar sink) { foreach (c; source) sink.put(c); }
As long as there are StdinByChar and StdoutByChar types, you can copy / paste to other types:
Use template
void copy(Source, Sink)(ref Source source, ref Sink sink) { foreach (c; source) sink.put(c); }
Solved the general problem, but accepted any input type, resulting in disaster, plus constraints:
Sink copy(Source, Sink)(ref Source source, ref Sink sink if (isInputRange!Source && isOutputRange!(Sink, ElementType!Source { foreach (c; source) sink.put(c); return sink; }
This is our first algorithm! (returned for composability.)
current state
StdinByChar source; StdoutByChar sink; copy(source, sink);
Add ufcs: func (a, B, c), which can be written as: a.func(b,c). Now it is as follows:
StdinByChar source; StdoutByChar sink; source.copy(sink);
Filtering:
int[] arr = [1,2,3,4,5]; auto r = arr.filter!(a => a < 3); writeln(r);
Output:
[1, 2]
Mapping:
int[] arr = [1,2,3,4,5]; auto r = arr.map!(a => a * a); writeln(r);
Output:
[1, 4, 9, 16, 25]
Simplification:
int[] arr = [1,2,3,4,5]; auto r = arr.reduce!((a,b) => a + b); writeln(r);
Output:
15
Linked together:
import std.stdio; import std.array; import std.algorithm; void main() { stdin.byLine(KeepTerminator.yes map!(a => a.idup). array. sort. copy(stdout.lockingTextWriter()); }
Features are also required:
● handling wrong exceptions
● template function
● formwork constraints
● ufcs (Unified commissioning)
● interval is a concept, not a type
● inline, customized, optimized
● limit
● derivation type
● tuple
conclusion
● components are a way to reuse code
● components require traditional and language support
● many advanced features of D can be combined to support components
● build on the early success of file and filtering, streaming and stl