Component programming in d language

Posted by Coronach on Mon, 25 Oct 2021 04:20:23 +0200

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

sourcealgorithmRemit
filesortfile
treefiltertree
arraymaparray
socketreducesocket
listmaxlist
iotasearch
random numberOdd 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