четверг, 13 августа 2015 г.

#819. Table of contents. English

Instead of greetings…

Link. Using the static analysis FixInsight to improve the code quality on Delphi

Briefly. I exported the dictionaries
Briefly. I made type-checking for array elements
Briefly. I finished words redefining

Follow up to “peculiarities of Supports"
Again about Supports
Briefly. Peculiarities of Supports

ToDo. Write about arrays, lists and iterators
Briefly. I made axiomatic description partly on Delphi, partly on scripts
Scripts, iterators and exception handling (draft)
Wide use of interfaces “in general” and InterlockedIncrement/InterlockedDecrement in particular...
A sort of "hungarian notation". Some more trivial staff
Classes, factories and interfaces
ToDo. Interfaces with partial implementation in the model

Some more about scripts

Briefly. Wonder of words redefining

ToDo. Changing text EVD parsing to parsing of special axiomatic scripts

How to test the "totally untestable"
How to test “untestable” applications

About RS, time pressure and forgetting
How to keep labor productivity up and not to lose self-respect
Briefly. “Shooting the breeze”. The “practices” I use writing the code

Testing of calculator. Table of contents
Testing of calculator №0. The beginning
Testing of calculator №1. Configuring of infrastructure. Adding of DUnit
Testing of calculator №2. Adding of business-logic test using visual controls
Testing of calculator №3. Expanding of test coverage
Testing of calculator №4. Changing of application’s architecture
Testing of calculator №5. Testing through “new architecture”
Testing of calculator №6.1. Testing using etalons
Testing of calculator №6.2. Testing using pseudo-random data
Testing of calculator №6.2.1. Applying of “classical TDD”
Testing of calculator №6.2.2. Processing of false data and boundary conditions
Testing of calculator №7. Comparing of floating-point numbers. Details about tests architecture

GUI-testing. Table of contents
GUI-testing 1. About tests and specially fitted “checkpoints”
GUI-testing 2. Thinking of testing
GUI-testing 3. Thinking of testing №2
GUI-testing 4. Thinking of testing №3
GUI-testing 5. Thinking of testing №4
GUI-testing 6. Thinking of testing №5
GUI-testing 7. Briefly. About GUI-testing “in the spoken style”
GUI-testing 8. About GUI-testing “in spoken style”. The follow-up
GUI-testing 9. GUI-testing “in spoken style”. Introducing of the alphanumeric characters to the current control
GUI-testing 10. GUI-testing “in the spoken style”. Note about “how this thing is organized”
GUI-testing 11. GUI-testing "in the spoken style". How this thing is organised №2
GUI-testing 12. GUI-testing "in the spoken style". Back to the basics
GUI-testing 13. GUI-testing “in spoken style". Back to the basics. Example of pressing the button of the form through the script
GUI-testing 14. GUI-testing “in the Russian style”. Adding DUnit to our “scripts”

What else I want to tell about TDD (not finished)
Once again about "testing levels"

About containers. Table of contents
Containers 1. Implementation of reference counting
Containers 2. My own implementation of IUnknown and reference counting. And mixins
Containers 3. Generics and without Generics...
Containers 4. Encapsulation of the work with the memory. Part 0
Containers 5. Encapsulation of the work with the memory. Part 1
Containers 6. Abstract containers
Containers 7. Abstract containers. Part 2
Containers 8. Deriving of specific atomic containers from abstract ones
Containers 9. Special containers. Part 2
Containers 10. About patterns and mixins
Containers 11. A little about using DUnit
Containers 12. DUnit patterns and tests

MindStream. How we develop software for FireMonkey. Table of contents
MindStream. How we develop software for FireMonkey
MindStream. How we develop software for FireMonkey. Part 2
MindStream. How we develop software for FireMonkey. Part 3. Firemonkey + DUnit
Link. Firemonkey + DUnit
MindStream. How we develop software for FireMonkey. Part 3.1. Inspired by GUIRunner
MindStream. How we develop software for FireMonkey. Part 4. Serialization
MindStream. Part 5. Testing

Another our screenshot
Some screenshot of our application

Briefly. Weak reference to interface

Briefly. About “partially initialized objects” (or again about factories)
Briefly. About constructors
Briefly. Again about factories
Briefly. About factories
Briefly. About triggering the exceptions

Briefly. About “reverse stability”

Briefly. Some more "reasoning about RAII"

Link. Why UML (for Delphi users)

Objective-C and Delphi

How to find out the true size of the memory allocated for by GetMem

Launch on the emulator
How I built friendship between Delphi XE7 with Samsung devices

About "refactoring"

Link. Threaded code

Link. If there were an automatic memory manager (i.e. a garbage collector) for the version of Delphi you currently use what would you be willing to pay for it?

Offtopic. Briefly. About similarities
They ask me…
Two quotes…

Briefly. Again about dependency inversion
Briefly. Shooting the breeze. Dependency inversion

Today it seemed to me I found an error in Delphi XE memory manager

Why I pay so much attention to mixins and tests

Lyrical digression
Depression, or Falsity of hResult and other ErrorCode’s

To be honest… Delphi for Android "evokes no enthusiasm "...

Link. Anonymous Methods and Captures in VCL

вторник, 11 августа 2015 г.

Briefly. I finished words redefining

Original in Russian: http://programmingmindstream.blogspot.ru/2015/08/blog-post_5.html

Follow-up to Briefly. Wonder of words redefining

Now this code:

USES
 axiom:ComboBox
 axiom:ComboTree
;
 
REDEFINITION
 : pop:ComboBox:DropDown
  OBJECT IN aCombo
  if ( aCombo Is class::TvtComboTree ) then
   ( aCombo pop:ComboTree:DropDown )
  else
   ( aCombo inherited )
 ; // pop:ComboBox:DropDown


can be rewritten in this way:

USES
 axiom:ComboBox
 axiom:ComboTree
 axiom:ComboTreeEx
;
 
REDEFINITION
 : pop:ComboBox:DropDown
  OBJECT IN aCombo
  callIfAccepted aCombo [ 
   [ @ class::ComboTreeEx @ pop:ComboTreeEx:DropDown ]
    // pop:ComboTreeEx:DropDown can be called if aCombo is compliable with ComboTreeEx
   [ @ class::ComboTree @ pop:ComboTree:DropDown ]
    // pop:ComboTree:DropDown can be called if aCombo is compliable with ComboTree
   [ nil @ inherited ]
    // can be called if all other branches do not pass 
  ] // callIfAccepted aCombo
 ; // pop:ComboBox:DropDown

It seems so simple “under hood”.

I will write later. We can even write in this way:

USES
 axiom:ComboBox
 axiom:ComboTree
 axiom:ComboTreeEx
;
 
REDEFINITION
 : pop:ComboBox:SetValue
  ANY IN aValue
  OBJECT IN aCombo
  callIfAccepted [ aValue aCombo ] [ 
   [ @ INTEGER @ class::ComboTreeEx @ pop:ComboTreeEx:SetValue ]
    // pop:ComboTreeEx:DropDown can be called if aCombo is compatible with ComboTreeEx,
    // and aValue is compatible with INTEGER
   [ @ STRING @ class::ComboTree @ pop:ComboTree:SetValue ]
    // pop:ComboTree:DropDown can be called if aCombo is compatible with ComboTree,
    // and aValue is compatible with STRING
   [ @ ANY nil @ inherited ]
    // can be called if all other branches do not pass
  ] // callIfAccepted aCombo
 ; // pop:ComboBox:DropDown

We can make it more simple aiming to minimize using of @.

We can do in this way:

USES
 axiom:ComboBox
 axiom:ComboTree
 axiom:ComboTreeEx
;
 
REDEFINITION
 : pop:ComboBox:SetValue
  ANY IN aValue
  OBJECT IN aCombo
  callIfAccepted [ aValue aCombo ] [ 
   [ @ INTEGER @ class::ComboTreeEx @ pop:ComboTreeEx:SetValue ]
    // pop:ComboTreeEx:DropDown can be called if aCombo is compatible with ComboTreeEx,
    // and aValue is compatible with INTEGER
   [ @ STRING @ class::ComboTree @ pop:ComboTree:SetValue ]
    // pop:ComboTree:DropDown can be called if aCombo is compatible with ComboTree,
    // and aValue is compatible with STRING
   [ @ STRING @ class::ComboBox @ inherited ]
    // inherited can be called if aCombo is compatible with ComboBox,
    // and aValue is compatible with STRING
   [ @ ANY @ ANY @ NotImplemented ]
    // can be called if all other branches do not pass
    // and causes error NotImplemented
  ] // callIfAccepted aCombo
 ; // pop:ComboBox:DropDown

Or in this way:

USES
 axiom:ComboBox
 axiom:ComboTree
 axiom:ComboTreeEx
;
 
REDEFINITION
 : pop:ComboBox:SetValue
  ANY IN aValue
  OBJECT IN aCombo
  callIfAccepted [ aValue aCombo ] [ 
   @[ INTEGER class::ComboTreeEx pop:ComboTreeEx:SetValue ]
    // pop:ComboTreeEx:DropDown can be called if aCombo is compatible with ComboTreeEx,
    // and aValue is compatible with INTEGER
   @[ STRING class::ComboTree pop:ComboTree:SetValue ]
    // pop:ComboTree:DropDown can be called if aCombo is compatible with ComboTree,
    // and aValue is compatible with STRING
   @[ STRING class::ComboBox inherited ]
    // inherited can be called if aCombo is compatible with ComboBox,
    // and aValue is compatible with STRING
   @[ ANY ANY NotImplemented ]
    // can be called if all other branches do not pass
    // and causes error NotImplemented
  ] // callIfAccepted aCombo
 ; // pop:ComboBox:DropDown

It minimizes using of @.

Briefly. I made type-checking for array elements

Original in Russian: http://programmingmindstream.blogspot.ru/2015/08/blog-post_4.html
Follow-up to - ToDo. Write about arrays, lists and iterators

Briefly. I made type-checking for array elements.

Something like this:

ARRAY VAR A
ARRAY OF STRING VAR B
A := [ 1 2 3 'hello' ] // - compilable
B := [ 1 2 3 'hello' ] // - not compilable

Defining of type

ARRAY VAR A
ARRAY OF STRING TYPE StringArray // - this is an alias for ARRAY OF STRING
StringArray VAR B
A := [ 1 2 3 'hello' ] // - compilable
B := [ 1 2 3 'hello' ] // - not compilable

To make it more complex:

STRING FUNCTION X
 Result := 'hello'
;
 
ARRAY VAR A
ARRAY OF STRING TYPE StringArray // - this is an alias for ARRAY OF STRING
StringArray VAR B
A := [ 1 2 3 X ] // - compilable
B := [ 1 2 3 X ] // - compilable, but throws the Run-Time error

Adding a reference to the function:

STRING FUNCTION X
 Result := 'hello'
;
 
ARRAY VAR A
ARRAY OF STRING TYPE StringArray // - this is an alias for ARRAY OF STRING
StringArray VAR B
A := [ 1 2 3 @ X ] // - compilable
B := [ 1 2 3 @ X ] // - not compilable

Obviously, next, I’ll make output of types and cut the number of Run-time errors in favor of Compile-time.

One more example:

ARRAY VAR A
ARRAY OF STRING TYPE StringArray // - this is an alias for ARRAY OF STRING
ARRAY OF INTEGER TYPE IntegerArray // - this is an alias for ARRAY OF INTEGER
StringArray VAR B
IntegerArray VAR C
A := [ 1 2 3 'hello' ] // - compilable
B := [ 1 2 3 'hello' ] // - not compilable
C := [ 1 2 3 'hello' ] // - not compilable
 
StringArray VAR B1
IntegerArray VAR C1
B1 := [ '1' '2' '3' 'hello' ] // - compilable
C1 := [ 1 2 3 4 ] // - compilable

Sure, there are also ARRAY OF ARRAY OF X:

ARRAY OF ARRAY OF STRING VAR A
A := [ [ 'a' 'b' ] [ 'c' 'd' ] ] // - compilable
A := [ [ 'a' 'b' ] [ 'c' 3 ] ] // - not compilable

The similar is for Run-time:

INTEGER FUNCTION X
 Result := 3
;
 
ARRAY OF ARRAY OF STRING VAR A
A := [ [ 'a' 'b' ] [ 'c' 'd' ] ] // - compilable
A := [ [ 'a' 'b' ] [ 'c' 'd' ] [ 'e' ] ] // - compilable, dimension is not critical
A := [ [ 'a' 'b' ] [ 'c' X ] ] // - compilable, but throws the Run-Time error

Keeping in mind Briefly. I made axiomatic description partly on Delphi, partly on scripts , I can write part of the code on Delphi, part of it on scripts and integrate it all in EXE-file.

At this point, we can use lambdas, iterators and other “delicacies” on Delphi7.


Instead of greetings…

Original in Russian: http://18delphi.blogspot.ru/2013/03/blog-post.html





I have been programming on Delphi for 18 years. Among other things, I know C, C++, Objective-C, asm PDP-11, asm-Intel86, FORTH and some "gibberish" languages. I want to share the TECHNICAL details of my work as a programmer and my thoughts about programming and architecture.


Apart from it, I am very interested in TDD and UML.


Here and there they say “who on earth need it?” in regard to TDD :-) I’ve almost got used to it.


As a matter of fact, GOOD programmers are the FIRST who need testing. Or, perhaps, BAD ones... Surely, not genios... who can not PREDICT their code’s behaviour when making the SLIGHTEST changes to it.


I’ll give an example. I have spent long and tiresome but interesting time on programming the Everest editor. A COMMERCIAL product was made on it.


So, one of the Everest’s features is printing and print preview. How we can debug it?


Suppose documents contain several thousands pages. Print? Several PACKS of paper?


Virtual printer is also useless.


I have been editing the code of the “print engine” for a VERY long time. And so I got trapped in the “programmer’s swing”: you edit here, it fails there, you edit there, it fails here… This is ENDLESS... My brain was so exhausted... It felt I could kill somebody... The functionality was not working...


Moreover, the Quality Group pointed at errors one after another… The Quality Group was my “Scourge of God”! They detected my errors FASTER than I could fix them...


Here and there appeared pages in the document that could not be printed or where the application simply failed....

That was the point I thought about TESTING... I was scared and terrified... I wanted to COVER all functionality with tests... I could not come up with the idea how to do it... Thoughts were tumbling in my brain.. I though about “how to find a SUPER-test to cover ALL AT ONCE"....


I felt like beating my head over the wall...

But I found a wise man...


Aleksandr Mozhaev... He said, “there exist DUnit, the Unit-tests for Delphi... you probably know... nothing extraordinary… but very useful..." I DID NOT KNOW...


I decided to try....


When I “looked into” DUnit, I was DISAPPOINTED... It was not only far from extraordinary - it was SIMPLE and “flat”...


"I can do this too"...


I asked Mozhaev, “so what?”...

He said, “try and you’ll like it"...

I shrugged my shoulders and decided to try...


By that moment, I had already written several frameworks... I actually thought that “I know how to teach programming to everybody" :-)


(sarcasm here)


I began to play with DUnit...


"Nothing extraordinary".. "Silver bullet here?"


I see published-methods that are “somehow” shown in “some” tree... So what?


However, I decided that if they advise it is worth trying...

I played further... I wrote MY FIRST TEST (not the very first in my life, but the FIRST for DUnit).. It printed the text... Or, rather did “print preview”, through the virtual canvas...


The test passed :-) I saw the “green light”.


Next, I worked on a “spherical cow in a vacuum”... I tried to launch this test automatically... I was DISPAPPOINTED AGAIN...


Thing is, I thought we could just launch exe-file and specify a run-time key for it: "come on, launch the test automatically"....

YOU WISH !!!


IT APPEARED there were not ANY key...


And so, I began to “grokk the source code" (as said my colleague).


After a thoughtful "grokking" I learned that, in order to execute tests automatically, we need to compile ANOTHER exe-file in consol mode (not the GUITestRunner but ConsoleTestRunner!).


"What the devil!", I said to myself, “these authors of DUnit must have me by the tale"....


They have ME, the man who managed “gibberish” language and Rational Rose (sarcasm again) by the tale?!!!


But then, I thought… and thought (and I liked it). I thought about, actually why I can not have the GUITestRunner and ConsoleTestRunner in ONE application. It turned out I CAN NOT. Though, it was just TECHNICAL complexity. I solved the issue.


I mate a hedgehog and a snake.. I mean I combined GUITestRunner and ConsoleTestRunner and refined them. The ConsoleTestRunner has got a child class KTestRunner. Back then I was already thinking about publishing the results of tests in our “knowledge base” and bug-tracker.


As a result, tests (ONE test) worked either both manually and automatically (if command-line option ItsLAW was set).


Next, I added automatic tests to automatic launch from Scheduler and I was HAPPY.


i was happy for I managed to make this staff work, not because I understood “why I need it".


Till the moment... Till the moment when the TEST FAILED!!! :-) That is how I UNDERSTOOD!! LITERALLY the light came on me!


I edited “a regular boring error from Quality Group", again about PRINTING...


I edited it THE DAY BEFORE, CLOSED the error and went home to SLEEP happy.


When I came in the morning, MY FIRST TEST FAILED!!!


I was kind of struck by the lightning!!! This is it!! Now I know WHY!!!


I decided to puzzle out ‘what is the matter and what I broke yesterday".


And so (bless my heart!!!) I made a WISEST thing... I WOULDN’T EDIT the code... I made a SECOND TEST... You’re right... It is VERY SIMPLE when you know... But I DID NOT KNOW then...


I made (I repeat) the SECOND TEST based on the error closed the previous day and checked and closed by the delighted Quality Group.


SURE, the SECOND TEST passed...


The FIRST TEST still failed as before...


I spent the whole next week trying to find out what I broke in the FIRST TEST (the Quolity Group did not find yet). It gave me no peace...

I launched the test manually and pushed different buttons... You see, I am a BAD programmer, I can not solve the task with my INTELLECT (I tried), I can only “push the buttons”....


At some point… at some point… at some point… I PUSHED THE RIGHT button… The FIRST TEST PASSED!!!


I launched it once and ONCE AGAIN - it PASSED...


(Let me note, I launched tests manually - not ALL at once but ONE test, the FIRST one).


I committed the code, wrote the error and closed it and WENT HOME happy....


When I came to work the next day I was once again DISAPPOINTED...


The SECOND TEST failed :-(


Again, I felt like beating my head over the wall...


But I collected myself a man (sarcasm again) :-) and ran the tests again and again...


The “number one” (peg-legged) passed, the “number two” (bare-feet) was resistant to pass...


My pride of “genios programmer” and Rational Rose handler was wounded by this ‘little bare-feet savage", so I decided to take it up seriously...


The WHOLE day I sought for the cause of failure and could not find it... At long last, by 12 AM I found it... It was a “little comma”... What a comma was it!!! Silver bullet pales in comparison with it. I found it, though.


And so, NUMBER TWO passed!!!


I was beyond happy.

I committed the code and wanted to go home… I left the room, closed the door… I went to the parking...

An EQUIPOSE OF DOUBT was in me... I knew I ‘did not do something”...


So I came back...


And I ran BOTH TESTS automatically...


NUMBER TWO worked, but NUMBER ONE failed :-(


My pride was wounded again..


I totally did not want to go home...


"The swing", came to my mind.


"These bloody programmer’s swing"...


So I stayed there....


The error revealed itself... I was “in stream” and I KNEW what I had edited in NUMBER ONE and in NUMBER TWO... So I analysed the changes...


I FIGURED it out!!!


I changed and ran NUMBER ONE - it PASSED...


Then I ran NUMBER TWO - it PASSED TOO...


I went HOME happily...


Now I KNEW WHAT TO DO and what for...


----------------------------

You’d ask if “regression tests” are the issue...
"Sure!!", I’d say...


But! A bad genius programmer and Rational Rose handler DID NOT know about it, although he read a “million of articles” in which “wise men” tried to explain it to him...


But not I KNOW PRECIESLY WHAT TO DO...


---------------------------


Now we’ve got about 2000 "regression" tests that run EVERY night... EVERY day on different machines... A handful of machines... We don’t KNOW WHAT TO DO... Most important is we can QUICKLY respond when being “in stream”... as long as the “yesterday’s” changes are live and familiar... More often, I can FIGURE it out...


----------------------------


We’ve got no more than 100 printing tests and we don’t need a SUPER test that “covers all”... So we sleep in peace :-)


------------------------------


P.S. Next time, if I don’t quiet down, I’ll tell you how I got familiar with VGScene (ака FireMonkey), how I cursed it, how slow I was to enter the “layer-cake” of styles... I’ll also tell about Anton (unknowingly) putting me in the understanding the genios architecture of this library...


P.P.S. I’d also like to tell how we located the applications for Delphi and our conclusions.


P.P.P.S. I’d like to tell how I made FORTH-machine for testing the application for DUnit on the knee, how Dima wrote binding and the results.


P.P.P.P.S. It would be interesting to tell about programming on UML vs. "prototyping on UML" and the metaphor of  "microchips items" vs. "logic compiling on triggers".


P.P.P.P.P.S. Sure, I’d like to tell about my lifetime project, Everest editor, its technical details and solutions born when I developed it.

Briefly. I exported the dictionaries

Original in Russian: http://programmingmindstream.blogspot.ru/2015/07/blog-post_29.html

Follow-up to - Briefly. Wonder of words redefining.

The example:

Here is our code:

UNIT uA
 
PROCEDURE A
...
; // A
 
...
 
UNIT uB
 
USES
 uA
;
 
PROCEDURE B
...
 A // - compilable due to USES uA
; // B
 
UNIT uC
 
USES
 uB
;
 
PROCEDURE C
...
 B // - compilable due to USES uB
 A // - NOT compilable due to no USES uA
; // C

How can we improve it?

Here’s an option:

UNIT uA
 
PROCEDURE A
...
; // A
 
...
 
UNIT uB
 
USES
 uA
;
 
PROCEDURE B
...
 A // - compilable due to USES uA
; // B
 
UNIT uC
 
USES
 uA
 uB
;
 
PROCEDURE C
...
 B // - compilable due to USES uB
 A // - compilable due to USES uA
; // C

Another option:

UNIT uA
 
PROCEDURE A
...
; // A
 
...
 
UNIT uB
 
USES
 uA
;
 
EXPORTS uA // - exports module uA, it would be visible for THOSE containing OUR module uB
 
PROCEDURE B
...
 A // - compilable due to USES uA
; // B
 
UNIT uC
 
USES
 uB
;
 
PROCEDURE C
...
 B // - compilable due to USES uB
 A // - compilable due to EXPORTS uA in module B
; // C

Why do we need it?

It is very useful for writing the “facade layers” in case we need to isolate layers, on the one hand, and let them "do more than they can"? on the other hand.

It is really useful.

It is more so since we have the FORGET word that allows making the word not visible.

There is also a structure:

EXPORTS *

It means “export all dictionaries added to our dictionary through USES".

There is another structure:

EXPORTS A :: X

This one means “export the X word in the A dictionary from our dictionary”.

There is one more structure:

EXPORTS A :: X AS Y

This one means “export the X word in the A dictionary from our dictionary with the name Y”.

Actually, there is a problem.

We’ve got Enum = (one, two, three) in the module A.

The B module defines the X function that works with Enum.

It is clear that B does USES A and can see the members of Enum.

Finally, the C module does USES B and uses the X function.

So, in order to use the elements of Enum, it has to do USES A.

The aim is to “see” one, two, three.

We can also do EXPORTS A in the B module or EXPORTS A :: Enum.

As a result, the C module will not have to do USES A.

It will see what it needs using due to USES B and EXPORTS A :: Enum.

When I was writing these USES with my hands - that is quite fun… But when you place arrows on the model that is crazy...

That is where EXPORTS come for help.

Yes, C++ has through include, but there are other problems about architecture layers isolation.

I understand why it has something in common with Python.




Link. Using the static analysis FixInsight to improve the code quality on Delphi

Original in Russian: http://programmingmindstream.blogspot.ru/2015/07/fixinsight-delphi.html

Video in Russian.

Deserves high appreciation.

“Two cents” from me -  A sort of hungarian notation. Some more rinky-dink staff. 

By the way, Roman is mistaken as for interfaces and GUID.

If an interface does not have GUID, then Supports will not be compiled.

They’ll say:

"Interface has no GUID".

There are bigger problems with Supports:

Again about Supports
Wide use of interfaces “in general” and InterlockedIncrement/InterlockedDecrement in particular…
Follow-up on “peculiarities of Supports"
Briefly. Peculiarities of Supports

IfDef _FIXINSIGHT_ is not clear as well. Who defines the symbol? Can we forget to define this symbol and break the project?

It is also funny to hear about 50 or 60 "forms" or "units".

How about some (dozens, hundreds) thousands?

Can we use a command-line utility and run a “megaproject” for Delphi7?

Seems quite possible.

Where can we get it? :-)

"It does not exclude from compilation" – that is a GOOD QUESTION. Actually, YES, it EXCLUDES.

It seems there is a confusion about IfDef and IfNDef.

Actually - YES. Confusion. The explanation follows.

It also depresses me that the analysis covers only the files DIRECTLY added to the project.

среда, 5 августа 2015 г.

Follow up to “peculiarities of Supports"

Original in Russian: http://programmingmindstream.blogspot.ru/2014/04/supports_4.html

The previous series was here – Briefly. Peculiarities of Supports (in Russian)

I was asked here:

http://programmingmindstream.blogspot.ru/2014/04/supports.html?showComment=1396620657041#c4522417805967527391 (in Russian)

Quote of the question: "Excuse me, but I do not understand why we expect С if it is a method of the TC class. We do not create an object, right?"

I’ll develop the theme.

So, we had an example:

type
 ISomeInterface = interface
  procedure SomeMethod;
 end;//ISomeInterface
 
 TA = class(TObject, ISomeInterface {IUnknown is omitted FOR PURPOSE here})
  function _AddRef: Integer;
  function _Release: Integer;
  function QueryInterface(const anID: TGUID; out anObj): hResult; virtual; 
  procedure SomeMethod;
 end;//TA
 
 TB = class(TA)
  function QueryInterface(const anID: TGUID; out anObj): hResult; override;
 end;//TB
 
 TC = class(TIntefacedObject, ISomeInterface)
  procedure SomeMethod;
 end;//TC
 
...
 
function TA._AddRef: Integer;
begin
 Result := -1;
end;
 
function TA._Release: Integer;
begin
 Result := -1;
end;
 
function TA.QueryInterface(const anID: TGUID; out anObj): hResult;
begin
 if Self.GetInterface(anID, anObj) then
  Result := S_Ok
 else
  Result := E_NoInterface;
end;
 
procedure TA.SomeMethod;
begin
 Write('A');
end;
 
function TB.QueryInterface(const anID: TGUID; out anObj): hResult;
begin
 if IsEqualGUID(anID, ISomeInterface) then
 begin
  Result := S_Ok;
  ISomeInterface(Obj) := TC.Create;
 end//IsEqualGUID(anID, ISomeInterface)
 else
  Result := inherited QueryInterface(anID, Obj);  
end;
 
procedure TC.SomeMethod;
begin
 Write('C');
end;
 
...
var
 l_A : ISomeInterface;
 l_B : ISomeInterface;
 A : TA;
 B : TB;
begin
 A := TA.Create;
 B := TB.Create;
 if not Supports(A, ISomeInterface, l_A) then
  Assert(false);
 l_A.SomeMethod; // - A is seen in console
 if not Supports(B, ISomeInterface, l_B) then
  Assert(false);
 l_B.SomeMethod; // - A is seen in console, and we need C
end;

Let us write in THIS way:

type
 ISomeInterface = interface
  procedure SomeMethod;
 end;//ISomeInterface
 
 TA = class(TObject, IUnknown {IUnknown APPEARS here}, ISomeInterface)
  function _AddRef: Integer;
  function _Release: Integer;
  function QueryInterface(const anID: TGUID; out anObj): hResult; virtual; 
  procedure SomeMethod;
 end;//TA
 
 TB = class(TA)
  function QueryInterface(const anID: TGUID; out anObj): hResult; override;
 end;//TB
 
 TC = class(TIntefacedObject, ISomeInterface)
  procedure SomeMethod;
 end;//TC
 
...
 
function TA._AddRef: Integer;
begin
 Result := -1;
end;
 
function TA._Release: Integer;
begin
 Result := -1;
end;
 
function TA.QueryInterface(const anID: TGUID; out anObj): hResult;
begin
 if Self.GetInterface(anID, anObj) then
  Result := S_Ok
 else
  Result := E_NoInterface;
end;
 
procedure TA.SomeMethod;
begin
 Write('A');
end;
 
function TB.QueryInterface(const anID: TGUID; out anObj): hResult;
begin
 if IsEqualGUID(anID, ISomeInterface) then
 begin
  Result := S_Ok;
  ISomeInterface(Obj) := TC.Create;
 end//IsEqualGUID(anID, ISomeInterface)
 else
  Result := inherited QueryInterface(anID, Obj);  
end;
 
procedure TC.SomeMethod;
begin
 Write('C');
end;
 
...
var
 l_A : ISomeInterface;
 l_B : ISomeInterface;
 A : TA;
 B : TB;
begin
 A := TA.Create;
 B := TB.Create;
 if not Supports(A, ISomeInterface, l_A) then
  Assert(false);
 l_A.SomeMethod; // - A is seen in console
 if not Supports(B, ISomeInterface, l_B) then
  Assert(false);
 l_B.SomeMethod; // - NOW C is seen in the console
end;

Do you understand me?

I have change only ONE line and how GREAT IS THE DIFFERENCE!

Let’s move on.

Now we write in this way:

type
 ISomeFakeInterface = interface
 end;//ISomeFakeInterface
 
 ISomeInterface = interface
  procedure SomeMethod;
 end;//ISomeInterface
 
 TA = class(TObject, ISomeInterface {IUnknown is omitted FOR PURPOSE here}, ISomeFakeInterface)
  function _AddRef: Integer;
  function _Release: Integer;
  function QueryInterface(const anID: TGUID; out anObj): hResult; virtual; 
  procedure SomeMethod;
 end;//TA
 
 TB = class(TA)
  function QueryInterface(const anID: TGUID; out anObj): hResult; override;
 end;//TB
 
 TC = class(TIntefacedObject, ISomeInterface)
  procedure SomeMethod;
 end;//TC
 
...
 
function TA._AddRef: Integer;
begin
 Result := -1;
end;
 
function TA._Release: Integer;
begin
 Result := -1;
end;
 
function TA.QueryInterface(const anID: TGUID; out anObj): hResult;
begin
 if Self.GetInterface(anID, anObj) then
  Result := S_Ok
 else
  Result := E_NoInterface;
end;
 
procedure TA.SomeMethod;
begin
 Write('A');
end;
 
function TB.QueryInterface(const anID: TGUID; out anObj): hResult;
begin
 if IsEqualGUID(anID, ISomeInterface) then
 begin
  Result := S_Ok;
  ISomeInterface(Obj) := TC.Create;
 end//IsEqualGUID(anID, ISomeInterface)
 else
  Result := inherited QueryInterface(anID, Obj);  
end;
 
procedure TC.SomeMethod;
begin
 Write('C');
end;
 
...
var
 l_A : ISomeInterface;
 l_B : ISomeInterface;
 A : TA;
 B : TB;
begin
 A := TA.Create;
 B := TB.Create;
 if not Supports(A, ISomeInterface, l_A) then
  Assert(false);
 l_A.SomeMethod; // - A is seen in console
 if not Supports(B, ISomeInterface, l_B) then
  Assert(false);
 l_B.SomeMethod; // - A is seen in console, and we need C
 
 if not Supports(ISomeFakeInterface(B), ISomeInterface, l_B) then
  Assert(false);
 l_B.SomeMethod; // - C is seen in console, TA DA!!!
end;

AMAZING!

Isn’t it?

UNSYMMETRY of the Supports method is OBVIOUS.

In my opinion...

"A takeaway":

overload is "harmful", “in general” and, in THIS CASE, “in particular”.

overload with “covariant” types is very harmful.

Generally speaking, we could handle with the Supports(IUnknown) method ONLY (if Borland's stead) instead of creating another VERY UNOBVIOUS method Supports(TObject).

Is the idea clear?

Let me note, it is not “for everybody”.

That is why, do not rush to comment before you “grokk” the problem.

I hope I helped.

P.S. If such “exercises” of objects vs. interfaces are INTERESTING, let me know. I have some thoughts in store.

P.P.S. I also hope you understand that GUID in description to interfaces are OMITTED. They can be added at Crtl-Shift-G.

P.P.P.S. On the topic:
More about QueryInterface (in Russian)
Again about Supports
Oh yeah! Supports (in Russian)

P.P.P.P.S. One of my readers kindly provided me with a compilable example - https://bitbucket.org/ingword/temp/src


Briefly. Peculiarities of Supports

Original in Russian: http://programmingmindstream.blogspot.ru/2014/04/supports.html

I will not go in for analysis or “spoon-feeding”.

I’ll just give an example.

Just “for your information”.

The code:

type
 ISomeInterface = interface
  procedure SomeMethod;
 end;//
 
 TA = class(TObject, ISomeInterface {IUnknown is omitted FOR PURPOSE here})
  function _AddRef: Integer;
  function _Release: Integer;
  function QueryInterface(const anID: TGUID; out anObj): hResult; virtual; 
  procedure SomeMethod;
 end;//TA
 
 TB = class(TA)
  function QueryInterface(const anID: TGUID; out anObj): hResult; override;
 end;//TB
 
 TC = class(TIntefacedObject, ISomeInterface)
  procedure SomeMethod;
 end;//TC
 
...
 
function TA._AddRef: Integer;
begin
 Result := -1;
end;
 
function TA._Release: Integer;
begin
 Result := -1;
end;
 
function TA.QueryInterface(const anID: TGUID; out anObj): hResult;
begin
 if Self.GetInterface(anID, anObj) then
  Result := S_Ok
 else
  Result := E_NoInterface;
end;
 
procedure TA.SomeMethod;
begin
 Write('A');
end;
 
function TB.QueryInterface(const anID: TGUID; out anObj): hResult;
begin
 if IsEqualGUID(anID, ISomeInterface) then
 begin
  Result := S_Ok;
  ISomeInterface(Obj) := TC.Create;
 end//IsEqualGUID(anID, ISomeInterface)
 else
  Result := inherited QueryInterface(anID, Obj);  
end;
 
procedure TC.SomeMethod;
begin
 Write('C');
end;
 
...
var
 l_A : ISomeInterface;
 l_B : ISomeInterface;
begin
 A := TA.Create;
 B := TB.Create;
 if not Supports(A, ISomeInterface, l_A) then
  Assert(false);
 l_A.SomeMethod; // - A is seen in console
 if not Supports(B, ISomeInterface, l_B) then
  Assert(false);
 l_B.SomeMethod; // - A is seen in console, and we need C
end;

Though “it is obvious”. Have a look at the code:

function Supports(const Instance: TObject; const IID: TGUID; out Intf): Boolean;
var
  LUnknown: IUnknown;
begin
  Result := (Instance <> nil) and
            ((Instance.GetInterface(IUnknown, LUnknown) and Supports(LUnknown, IID, Intf)) or
             Instance.GetInterface(IID, Intf));
end;

Why do I write?

The reason is, I have seen people who are very amazed by it. (I am not one of them.)

Though... I “pigged out”, too.

Again about Supports



Original in Russian: http://18delphi.blogspot.ru/2013/11/supports.html

Thoughts about - (in Russian)

For some reason, MANY people (if not anybody) regard my posts as “a seeking for absolute” or “an attempt to be a guru” and create “a cow in a vacuum”.

It is not AT ALL the case.

I do not seek for absolute and I am not trying to get a silver bullet or create “a spherical cow in a vacuum”.

Quite the reverse!

I am a PRACTICAL MAN,

a CRAFTSMAN,

with no HIGHER EDUCATION.

I am not trying to find “a universal recipe”.

Quite the opposite. Personally I think there is NO UNIVERSAL recipe.

I only describe what I faced IN PRACTICE.

I do not “pull” ANYBODY ANYWHERE. I do not force ANYTHING upon ANYBODY.

I’ve written a TRIVIAL thing about Supports - "can be done in this way", and "can also be done in this way".

Obviously, the solutions are not EQUIVALENTS.

One solution is MORE GENERAL and the other one is MORE EFFECTIVE.

I base on my PRACTICE, nothing more.

I hold DEEP PREJUDICE against Supports and QueryInterface.

I really do.

I try not to use them “all other things being equal”. For example, using the “technique” described at a link above. Generally speaking, there are lots of techniques for DIFFERENT “special cases”.

I am motivated by my PRACTICE, nothing more.

It is just that once interfaces appeared, everybody understood “WOW! Interfaces are cool”.

Everybody was “ENTHUSIASTIC” about using the interfaces in general and Supports in particular.

I was not a “black sheep”.

I had to pay for it a lot, with my time spent on debugging and work with profiler.

You see, time is the most VALUABLE thing. (People “closer to 40” would probably understand me.)

I’ll try to give an example.

In the era of the RAGE for interfaces (we were young and had “three sheets in the wind”) – I, BY MY OWN, created the following IDIOTIC structure:

TmyObject = class(TPersistent)
 f_Owner : TPersistent;
 
 function GetOwner: TPersistent; override;
 begin
  Result := f_Owner;
 end;
 
 procedure SetOwner(anOwner : TPersistent);
 begin
  f_Owner := anOwner;
 end;
  
 function QueryInterface(anIntf : TGUID; out Obj): hResult;
 begin
  if Self.GetInterface(anItf, Obj) then
   Result := S_OK
  else
  if Supports(f_Owner, anIntf, Obj) then
   Result := S_OK
  else
   Result := E_NoInterface;
 end;
end;//TmyObject 

What is this about?

Objects TEND TO have owners.
If an interface is called from an object, first we try to get an interface from it.
If we FAIL, we try to get the interface from the OWNER.

Let the recursion go on.

The code is sure IDIOTIC. I UNDERSTAND it NOW unlike 15 or so years ago.

If you call it idiotic at a glance – my hat’s off. Kudos to you. Stop to read an “old fool”...

However, “some time ago” it seemed to me this code is “not too bad at all”.

It seemed to me it “accomplished a mission”.

If we do not get the interface from the object, we try to get it from its owner – quite “logical”...

In most cases it “WORKS”.

Let’s slip the cases when using this approach caused “parasite interfaces”. Such cases were RARE and there were SPECIAL “crutches” and “grinds” for them...

But, GENERALLY, ALL worked PROPERLY.

AS INTENDED...

LATER ON, I spent long HOURS under debugger and profiler seeking to find the answer to “why it is SO slow”.

IT HAPPENED TO BE VERY SIMPLE – first of all, GetOwner – NOT THE FASTEST method (dynamic, but not virtual, if I am not mistaken), well, hell with it...

Objects NESTING WAS (and is) deep ENOUGH.

Another factor came in the picture.

We called for the interface from the object. It does not support the interface. So we ask from the parent and the next parent. They do NOT SUPPORT is, TOO. Same is through the ENTIRE CHAIN. As a result, we PASSED THROUGH THE ENTIRE CHAIN, performed lots of computation and got nil and E-NoInterface (which was to be expected).

As a matter of fact, there is a vast of Supports and QueryInterface’s of many sorts for VARIOUS PURPOSES.

ANY of these performs COMPUTING which is “actually” NOT NECESSARY.

As a result, profiler and debugger showed it to me and I finally GOT RID of this CHAOS.

It took “much valuable time” that I could spend on something else.

May be, I am a FOOL or an IDIOT to have created such a TERRIBLE CHAOS. May be you are more CLEVER than me and you’re lucky.

However, I have seen such TERRIBLE CHAOS of other developers “under different pretexts”.

So I am not so special.

If you are more clever than our “gang” of FOOLS and IDIOTS, then you are lucky :-)

Personally I “hold prejudice against” Supports and QueryInterface.

Should you need MORE examples, I’ll give them to you, not only from my personal experience.

Well...

I do not “pull” anybody anywhere, do NOT PREACH and don’t “try to help”...

I only want to SHOW MY OWN MISTAKES to prevent OTHER people from making them.

“Wise men learn by other’s faults, fools by their own”...

Though it is not true for Russians...

P.S. It is time to rename my blog to “Old fool’s notes”... It would be more close to the idea... People would treat me with “indulgence”… What you can expect of a poor man...




понедельник, 3 августа 2015 г.

ToDo. Write about arrays, lists and iterators

Original in Russian: http://programmingmindstream.blogspot.ru/2014/01/todo_24.html

"Array" and "iterator" are the two basic concepts.

"Array" is the data structure.
"Iterator" is the imperative construct for iteration of array items.

A simple example:
ARRAY VAR A := [ 1 2 3 4 5 ]
// - Array specification and initialization
FOR A WriteLn
// - simple array iterator.
//   Has TWO parameters - the array and the function under iteration.
// - in this example the numbers 1 2 3 4 5 will be displayed

revert - takes array as a parameter and returns its “reverse display”.

I mean the DISPLAY, not a COPY.

Example:
ARRAY VAR A := [ 1 2 3 4 5 ]
// - Array specification and initialization
FOR revert A WriteLn
// - in this example the numbers 5 4 3 2 1 will be displayed

join - takes two arrays as a parameters and returns their “display as if they were merged”.

Remember - the DISPLAY, not a COPY.

Example:
ARRAY VAR A := [ 1 2 3 4 5 ]
// - Array specification and initialization
FOR join A A WriteLn
// - in this example the numbers 1 2 3 4 5 1 2 3 4 5 will be displayed

One more example:
ARRAY VAR A := [ 1 2 3 4 5 ]
// - Array specification and initialization
FOR join A join A A WriteLn
// - in this example the numbers 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 will be displayed

Another example:
ARRAY VAR A := [ 1 2 3 4 5 ]
// - Array specification and initialization
ARRAY VAR В := [ 6 7 8 9 10 ]
// - Array specification and initialization
FOR join A B WriteLn
// - in this example the numbers 1 2 3 4 5 6 7 8 9 10 will be displayed

removeduplicates - takes an array as a parameter and returns its “display with remote duplicates”.

Example:
ARRAY VAR A := [ 1 2 3 4 5 ]
// - Array specification and initialization
FOR removeduplicates join A A WriteLn
// - in this example the numbers 1 2 3 4 5 will be displayed, 
//   as if we work with A only and not with join A A

One more example:
ARRAY VAR A := [ 1 2 3 4 5 ]
// - Array specification and initialization
ARRAY VAR В := [ 2 6 3 7 10 ]
// - Array specification and initialization
FOR removeduplicates join A B WriteLn
// - in this example the numbers 1 2 3 4 5 6 7 10 will be displayed

filter - takes TWO parameters - functor and array and returns “array display with elements that match the functor value”.

Example:
ARRAY VAR A := [ 1 2 3 4 5 ]
// - Array specification and initialization
FOR filter BOOLEAN ( IN anItem Result := anItem != 2 ) A WriteLn
// - in this example the numbers 1 3 4 5 will be displayed

Files is a specific instance of “arrays”.

Example:
FILE VAR F := OpenRead 'C:\somefile.txt'
// - File specification and initialization
FOR chars F Write
// - in this example the file content will be displayed by symbols

One more example:
FILE VAR F := OpenRead 'C:\somefile.txt'
// - File specification and initialization
FOR lines F WriteLn
// - in this example the file content will be displayed by symbols

Let’s get back to arrays.
sort - takes sorting functor and array as parameters and returns “display of array sorted by functor”.

Example:
ARRAY VAR A := [ 1 3 4 2 5 ]
// - Array specification and initialization
FOR sort < A WriteLn
// - in this example the numbers 1 2 3 4 5 will be displayed

Another example (the purpose is the same but it also illustrates that “any code” can be written in functor and not only the axiomatic operator):
ARRAY VAR A := [ 1 3 4 2 5 ]
// - Array specification and initialization
FOR sort BOOLEAN ( IN anItem1 IN anItem2 Result := anItem1 < anItem2 ) A WriteLn
// - in this example the numbers 1 2 3 4 5 will be displayed

Arrays folding:

Example:
ARRAY VAR A := [ 1 2 3 4 5 ]
// - Array specification and initialization
INTEGER VAR X := ( 0 FOR A + )
// - the X variable will have the value 0 + 1 + 2 + 3 + 4 + 5

Another example:
ARRAY VAR A := [ 1 2 3 4 5 ]
// - Array specification and initialization
INTEGER VAR X := ( 1 FOR A * )
// - the X variable will have the value 1 * 1 * 2 * 3 * 4 * 5

One more example (illustrates the fact that not only axiomatic operator but ANY user functor can be given):
ARRAY VAR A := [ 1 2 3 4 5 ]
// - Array specification and initialization
INTEGER VAR X := ( 0 FOR A AUTO ( IN anItem1 IN anItem2 Result := anItem1 + anItem2 ) )
// - the X variable will have the value 0 + 1 + 2 + 3 + 4 + 5

Another example:
ARRAY VAR A := [ 1 2 3 4 5 ]
// - Array specification and initialization
INTEGER VAR X := ( 0 FOR A AUTO ( IN anItem1 IN anItem2 Result := anItem1 + anItem2 + 10 ) )
// - the X variable will have the value 0 + 1 + 10 + 2 + 10 + 3 + 10 + 4 + 10 + 5 + 10

Additional inline functor.

process - takes two parameters - functor for processing of array item and the array.

It returns “display of array with applied functor”.

Example:
ARRAY VAR A := [ 1 2 3 4 5 ]
// - Array specification and initialization
FOR process AUTO ( IN anItem Result := anItem + 10 ) A WriteLn
// - in this example the numbers 11 12 13 14 15 will be displayed

One more example (combination of process and revert):
ARRAY VAR A := [ 1 2 3 4 5 ]
// - Array specification and initialization
FOR revert process AUTO ( IN anItem Result := anItem + 10 ) A WriteLn
// - in this example the numbers 15 14 13 12 11 will be displayed

Oh! I forgot something.

The AUTO modifier means that the type of expression will be displayed “automatically” (similarly to auto in C++11).

Iteration of the compiled code.

codearray - takes ONE parameter, a link to the compiled code and returns it as an “array display”.

ANY compiled code of the script engine we get as an “array”.

Example:
PROCEDURE P1
 ( 1 + 2 )
 ( 3 + 4 )
 ( 5 + 6 )
;
// - the procedure contains THREE atomic expressions - 1 + 2, 3 + 4, 5 + 6
//   Let’s iterate the expressions:
 
FOR codearray @ P1 ( IN anItem anItem DO WriteLn )
// - in this example the numbers 1 + 2 3 + 4 5 + 6 will be displayed

!!! The DO construct executes the word according to the link given.

Another example (combination of codearray and process):
PROCEDURE P1
 ( 1 + 2 )
 ( 3 + 4 )
 ( 5 + 6 )
;
// - the procedure contains THREE atomic expressions - 1 + 2, 3 + 4, 5 + 6
//   Let’s iterate the expressions:
 
FOR process ( IN anItem anItem DO ) codearray @ P1 WriteLn
// - in this example the numbers 1 + 2 3 + 4 5 + 6 will be displayed

- The purpose of this example is similar to the previous one, plus it illustrates the possibility to eliminate the “additional functor” of the “cycle body”.

Let’s illustrate it with the example:
ARRAY FUNCTION A
// - function that returns an array
 
 PROCEDURE P1
 // - P1 - is a procedure nested in the A function and it is not called explicitly anywhere, 
 //   only the LINK to it is used (below)
  ( 1 + 2 )
  ( 3 + 4 )
  ( 5 + 6 )
 ;
 
 Result := process ( IN anItem anItem DO ) codearray @ P1
; // A
 
FOR A WriteLn
// - in this example the numbers 1 + 2 3 + 4 5 + 6 will be displayed
//   All is same to the previous example, 
//   but the array is a result of the A function execution


The !!! @ operator returns a link to the word behind it.

One more example (like anonymous code):
ARRAY FUNCTION A
// - function that returns an array
 
 Result := process ( IN anItem anItem DO ) codearray @ ( 
  ( 1 + 2 )
  ( 3 + 4 )
  ( 5 + 6 )
 )
; // A
 
FOR A WriteLn
// - in this example the numbers 1 + 2 3 + 4 5 + 6 will be displayed
//   All is same to the previous example, but the array 
//   is a result of the A function execution 
//   that in its turn returns the array as a “display of the code 
//   of the anonymous function

In general, the constructs given are the analogue of the lists in Python.

Iteration of inline words (or analogue of RTTI).

membersarray - takes one parameter - a link to the word and returns “link to the array of inline items”.

Example:
PROCEDURE P1
 
 PROCEDURE P1.1
  1 + 2
 ; // P1.1
 
 PROCEDURE P1.2
  3 + 4
 ; // P1.2
 
 PROCEDURE P1.3
  5 + 6
 ; // P1.3
  
; // P1
 
FOR membersarray @ P1 ( IN anItem anItem DO WriteLn )
// - in this example the numbers 1 + 2 3 + 4 5 + 6 will be displayed

Anoter example (combination of membersarray and process):
PROCEDURE P1
 
 PROCEDURE P1.1
  1 + 2
 ; // P1.1
 
 PROCEDURE P1.2
  3 + 4
 ; // P1.2
 
 PROCEDURE P1.3
  5 + 6
 ; // P1.3
  
; // P1
 
FOR process ( IN anItem anItem DO ) membersarray @ P1 WriteLn
// - in this example the numbers 1 + 2 3 + 4 5 + 6 will be displayed

Here is an example (with the function that returns an array):
ARRAY FUNCTION A
 
 PROCEDURE P1
 
  PROCEDURE P1.1
   1 + 2
  ; // P.1.
 
  PROCEDURE P1.2
   3 + 4
  ; // P1.2
 
  PROCEDURE P1.3
   5 + 6
  ; // P1.3
 
 ; // P1
 
 Result := process ( IN anItem anItem DO ) membersarray @ P1
;
 
FOR A WriteLn
// - in this example the lines 1 + 2 3 + 4 5 + 6 will be displayed

Why RTTI?

We need it because:

wordname is a word taking a link to the word and returning its name.

Example:
PROCEDURE P1
 
 PROCEDURE P1.1
  1 + 2
 ; // P1.1
 
 PROCEDURE P1.2
  3 + 4
 ; // P1.2
 
 PROCEDURE P1.3
  5 + 6
 ; // P1.3
  
; // P1
 
FOR membersarray @ P1 ( IN anItem wordname anItem WriteLn )
// - in this example the lines P1.1 P1.2 P1.3 will be displayed

Another example:
PROCEDURE P1
 
 PROCEDURE P1.1
  1 + 2
 ; // P1.1
 
 PROCEDURE P1.2
  3 + 4
 ; // P1.2
 
 PROCEDURE P1.3
  5 + 6
 ; // P1.3
  
; // P1
 
FOR process ( IN anItem wordname anItem ) membersarray @ P1 WriteLn
// - in this example the numbers P1.1 P1.2 P1.3 will be given

Oh!!! Word IN defines the INPUT parameter.

The brackets () define the “anonymous word” by the own name space.

More about RTTI:

wordparent - takes a link to the word and returns it to the parent.

Example:
PROCEDURE P1
 
 PROCEDURE P1.1
  1 + 2
 ; // P1.1
 
 PROCEDURE P1.2
  3 + 4
 ; // P1.2
 
 PROCEDURE P1.3
  5 + 6
 ; // P1.3
  
; // P1
 
FOR process ( IN anItem wordname wordparent anItem ) membersarray @ P1 WriteLn
// - in this example the lines P1 P1 P1 will be displayed

wordproducer - takes a link to the word and returns it to the word that created it.

Example:
PROCEDURE P1
 
 PROCEDURE P1.1
  1 + 2
 ; // P1.1
 
 PROCEDURE P1.2
  3 + 4
 ; // P1.2
 
 PROCEDURE P1.3
  5 + 6
 ; // P1.3
  
; // P1
 
FOR process ( IN anItem wordname wordproducer anItem ) membersarray @ P1 WriteLn
// - in this example the lines PROCEDURE PROCEDURE PROCEDURE will be displayed

Similarly, we can iterate the input parameters and result values.

There are many more tricks.

Now you see why we need RTTI?

By the way - map-reduce (https://en.wikipedia.org/wiki/MapReduce) and Fold (https://en.wikipedia.org/wiki/Fold_(higher-order_function)) are related to what I described here, are not they?

For the final:

"User array":

FunctorToArray - takes one parameter - iteration functor and returns “display of array”.

Example:
ARRAY FUNCTION A
 Result := FunctorToArray ( FOR [ 1 2 3 4 5 6 7] YIELD )
;
 
FOR A WriteLn
// - the numbers 1 2 3 4 5 6 7 will be printed

Well... well... http://habrahabr.ru/post/132554/ (in Russian)

I quote:
"Your best friend Itertools

Unit itertools has special functions for iterated objects. Do you need to duplicate the iterator? Or combine two generators in sequence? Or group the values of inline lists in one line? Apply map or zip and do not create one more list?

Just add import itertools."

:-)

As always - "thinking about us".
----------------------------------------------------------------------

(+) http://habrahabr.ru/post/85238/ (in Russian)

(+)
numbers = [1,2,3,4,5]
squares = [number*number for number in numbers]

-- I can do that too.

Have a look:
numbers = [ 1 2 3 4 5 ]
squares = [ for numbers (IN anItem anItem * anItem) ]

(+) http://ninjaside.info/blog/ru/funkcii-map-i-zip-i-lambda-python/ (in Russian)