This paper will touch on the lack of mechanisms to pass arguments back from defined functions other than return values, if return values are already used as error codes. An extreme case is the use of call-back functions, or event procedures, to the events of GUI objects. In Dyalog APL/W, functionality of arguments and return values of call-back functions are already well-defined. The way you can pass information back from a call-back function, or event procedure, is by storing it as a global variable or by storing it into the Data property of an GUI object, which is also a global variable. This paper proposes a solution - utilize a Call-by-Reference mechanism of function arguments passing, in addition to return values of defined function, to pass results of defined functions back to calling functions. The programming environment is in Dyalog APL/W Version 7.1.3. This interpreter is robust itself and is feature-rich in terms of the integration of APL and MS Windows GUI environment. This paper starts with a description of problems encountered when programming in a GUI environment with call-back functions as well as the difference in design logic of programming styles among procedural programming and event-driven programming. Secondly, this paper presents a brief description of two different mechanisms to pass arguments into and out of defined functions. Thirdly, this paper presents a design solution to the problems mentioned above based on the Call-by-Reference mechanism described in second part. Lastly, a conclusion is made and potential pitfalls are explored.
All code segments demonstrated here are implemented and tested in Dyalog APL/W Version 7.1.3.
Modern operating systems provide the Graphical User Interface, GUI in short, to ease the learning efforts and daily work of users. Programs that use GUI environments run in event-driven fashion. In other words, your programs lay out GUI objects on screen, such as push buttons, edit fields, combo boxes, and grids. Your programs also define the actions to take when your users interact with these objects. One action acting upon an object generates a specific event associated with such an object. This event can be taken care of by the GUI environment with default actions if there is no action associated with it, or this event can be taken care of by call-back functions if you have already defined such a call-back function associated with the event. In this event-driven fashion, the design and execution of program logic is simple enough you define all the objects and the behaviors of objects associated with user actions. You give these objects to users. They interact with these objects and these objects respond with pre-defined and proper behaviors. On the other end as in traditional and procedural programming language, you set up loops and traps to deal with users actions so that user interaction is well handled. You have to define the logic to assure that the information among users and programs is interchanged in proper order. Program maintenance is also a painful process, even if youve written the codes yourself with documentation. In comparison, GUI design is a dream for programmers. It is simple and elegant. When equipped with the help of visual tools, you can finish the design of user interface and complete program logic as if you were drawing. The only challenges for programmers to tackle are the design of the interface and the writing of call-back functions.
As a full-time APL/W programmer, I encountered an awkward situation while developing a portfolio management system by utilizing GUI objects. Here are the program layouts for both procedural and GUI styles. Note that for the purpose of re-usage modules, the modular design of program blocks is heavily emphasized.
RInitialize. /* initialize user and database setup, return error code */
Error_Check R /* check Error code, and prompt user to determine what to do */
PortfolioPick_Portfolio /* create a form to query portfolio name, */
/* pricing date, and holding date, by giving users the options */
/* to choose from */
Error_Check Portfolio /* check return value to determine whether the user gives a good*/
/* selection and data availability */
HoldingLoad_portfolio portfolio /* Load holdings based on selected portfolio */
Error_Check Holding /* check return value to see data requested is available */
RDisplay_Holding /* display holdings of current selected portfolio on screen */
Error_Check R /* check return value of previous function, there might be */
/* resource problem so that holding screen could not be created */
After the portfolio is loaded, the on-screen display provides other functionality such as scenario analysis and report printing. The concept and layout is clear enough and it really fosters the re-usage of program codes. The maintenance of APL codes is also amazingly easy, except that it is very difficult to construct each block in APL/W with actual return values1. The reason and the solution are what this paper is all about.
RINITIALIZE © initialize user and database setup, return error code
ERROR_CHECK R © check the result of Initialization
LOAD_PORTFOLIO © give user the selections of portfolio, and call creation of
© on-screen portfolio display in call-back functions
DQ '.' © give user the control
It is a very clean design at first glance. The problem of this style lies inside the LOAD_PORTFOLIO function, which branches into a very complex call-back function tree2. Maintenance is a nightmare even if youve written the codes yourself.
It is clear that the first method is preferred if current APL implementation is equipped with extensions, the most obvious among those is the mechanism to return values back to calling functions other than formal return values of functions. Lets first look at the creation of defined functions in APL.
User written functions in APL are called defined functions, to distinguish them from APL system functions and APL primitive functions. Defined functions in APL allow a great flexibility in terms of calling conventions. Defined functions can either return values, or not. Defined functions can be monadic or dyadic. Defined functions can have shy arguments and, even shy return values. This kind of flexibility creates greater possibilities in programming style. For example, you can write a defined function to run differently depending on whether it has a left argument 3 or not, simply by defining its left argument as shy. If the left argument does not exist, it can assign its value internally in the function, which allows the program to initialize default values if not given by user. Another example is the shy return value. This type of defined function will return a value, or not, depending on whether there is another variable assigned the return value of such defined function.
Following are some code segments expressing this idea. Note that it is assumed that Dyalog APL/W Version 7.1.3 is the running interpreter.
RETURN
{R}RETURN ARG © Function defined with a right argument and shy return value
[1] RARG © Return a value the same as input argument if it returns
[2] ARGARG×3 © Modify input argument
RETURN 3 © Run the function without assigning return value
RETURN 4 © Same as above run
TRETURN 3 © Run the function with return value assigned to T
T © The value of T is assigned as input value of the function
3 ©
TRETURN 4 © Try another value
T ©
4 ©
I3 © Use a variable as the input argument
TRETURN I © Assign the return value to T again
I T © Check both input and return value
3 3 © Input is not changed since it is using Call-by-Value convention
By inspecting this APL code segment, it is clear that APL defined functions are powerful and unique in terms of their ability and flexibility to define shy arguments and return shy results. However, the ability to pass back modified values from APL defined functions is restricted as using return values only. It is not possible to define a variable as a place holder and pass it into defined functions as an argument so that the defined functions can hold the value of the variable after its execution. The later fashion of argument passing is commonly seen in API functions for developing GUI applications in modern GUI environments such as MS-Windows or Motif. For now, it is essential to make clear the different concepts of function calling conventions mentioned above. There are basically two mechanisms to pass values into defined functions.
The definition of Call-by-Value arguments is that the actual values passed into defined functions as arguments are only copies of original values, or the actual values of arguments. The scopes of these new copies/variables are strictly inside the functions. Memory used by local variables is actually allocated dynamically by the programming language runtime environments, either from the interpreter itself when programs run or by the operating system during runtime, if the environment is a compiled language. After the completion of the running of defined functions, even though you modify the values of these variables inside defined functions, these variables are destroyed and memory used is freed. The original values of these variables outside the function are not touched/changed. It is the default argument passing method for most programming languages except BASIC. BASICs default argument passing method is Call-by-Reference, which will be discussed later, and can be modified into this style by ByVal modifier of arguments. Another exception is, of course, APL, which has a Call-by-Value style only. The advantage of Call-by-Value is localization. The scope and life of local variables are absolutely restricted inside defined functions. If well defined, object name clashes never occur. This frees programmers of the burden of keeping track of the uses of object names. The disadvantage is not that clear if you dont care too much about the efficiency of the program. Some programs will definitely suffer, in terms of efficiency, in Call-by-Value only solution. A good example is a matrix transformation program. This program takes a matrix and returns another matrix, calculated/transformed, with the same shape as the given matrix. The logical actions taken from interpreter or compiler are like this:
Outside Matrix Transformation Matrix =: malloc(sizeof(Matrix)) (* the allocation of memory to store input matrix *) Result =: malloc(sizeof(Matrix)) (* the allocation of memory to store output matrix *) Enter program Matrix =: malloc(sizeof(Matrix)) (* the allocation of memory to store local variable *) Result =: malloc(sizeof(Matrix)) (* the allocation of memory to store local result *) Result =: process process (* assign calculation result into matrix *) Push(Result) (* to keep the result in stack used outside program *) Free(Matrix) (* free the memory which stores local input matrix *) Free(Result) (* free the memory which stores local result matrix *) Exit program Result =: pop() (* pop up result stored before *) memcopy(Result, Matrix) (* copy the contents from Result to Matrix *) Free(Result)
At least four memory allocations are required to finish this task. We will have similar actions from the Call-by-Reference convention for comparison later.
Though the handling of arguments and return values is powerful and unique enough in APL, another mechanism to pass arguments into and return values from defined functions, which is commonly seen in other programming languages such as C or Pascal, is missing. The missing part of the function calling mechanism is the Call-by-Reference (or Call-by-Address) convention, in comparison with Call-by-Value convention used among APL and other programming languages.
The definition of Call-By-Reference is different. The arguments passed into defined functions are addresses of those variables. That is, arguments are only the memory locations/addresses where the values are stored. Inside defined functions, you can reference to the values by pointing to the addresses instead. If you modify the values stored at the memory pointed to or referenced by the addresses passed in, you actually change the contents of these addresses. Values of such variables are changed irrelevant to whether inside or outside of this function. The Call-by-Reference convention of argument passing serves a couple of purposes. The first, this mechanism still provides a way to pass values between calling functions and those called, by means of referencing the same addresses. Second, Call-by-Reference convention provides place-holders for return values. By the method of creating empty/null variables with blanks or empty contents, you can pass in the addresses of these empty/null variables into a function and allow the calculated results of this function to fill the blanks. This usage is commonly seen in modern GUI developing environments as described before. Third, by using the arguments as a place-holder for return values in the Call-by-Reference convention, you can reserve function return values as error codes for error checking. Last, Call-by-Reference eliminates the overhead of memory re-allocations because only the pointers/references are passed into defined functions.
Lets take a look at the Matrix Transformation Problem again. The actions required for Call-by-Reference mechanism are:
Outside Matrix Transformation Matrix =: malloc(sizeof(Matrix)) (* the allocation of memory to store input matrix *) Enter program Matrix =: process process...... (* the result is put to the memory directly *) Exit program
The difference in terms of efficiency is huge if the matrix we are talking about is really large.
As long as the programming environment provides users with flexibility in calling conventions for argument passing for defined functions, programmers can always determine the best way to pass arguments.
Dyalog APL/W is currently the most advanced APL interpreter in terms of the integration of APL interpreter with Windows GUI environment. Dyalog APL/W facilitates the concept of Objects and builds the link between the APL interpreter and the GUI environment elegantly. All objects provided with Windows can be built/created from APL code. You can associate functions in DLL libraries provided by third party software vendors and integrate them as native APL functions. Each object has its own set of properties, which define the look and feel of this object. Properties include Size, FCol, Bcol, Caption, Font, and others. Each object also has various events, which define the relation between actions from users and reactions from this object. Events include MouseUp, Select, Changed, and others. APL programmers create objects, and define the reactions responding to these events in the form of call-back functions or, in another term, event procedures. Events are actually recorded by Windows and passed back to the APL interpreter as event messages. The APL interpreter receives these event messages and checks to see if any call-back functions/event procedures are associated with them. If no event procedures are defined, APL will pass event messages back to Windows and give back control to Windows so that Windows can update the object status with default actions 4.
One example of such behavior is an EDIT field. An EDIT field allows the user to type characters into the field. Whenever a character is typed inside EDIT field, Windows reacts, depending on whether there is a call-back function associated with KeyPress event. If no action is associated with this event, APL does nothing and passes control back to Windows to update the field with the user input charcacter. If this event is associated with a function UPPER, which translates lower case characters into upper case characters, APL invokes UPPER function when the user types a character. UPPER translates this character into the correct format and returns an updated/modified event message. Windows picks up the returned event message and updates the content of this EDIT field. The listing of function UPPER follows 5.
MSGUPPER MSG;I
[1] © Converts lower-case alphabets to upper-case
[2] I'abcdefghijklmnopqrstuvwxyz'¼3MSG
[3]
(I>26)/0
[4] MSG[3]IA
Note that each event has a different event message format. An event message serves as a record to keep track of the object name, event name, contents of user action, and other information, associated with each action upon an object .
APL/W offers great flexibility in terms of event processing. It allows immediate execution as the response to user events. You can have APL execute a literal string such as GOTO command or Assignment as the action of events. You can also use an integer number to prompt for APL and Windows to perform default actions to user events. Please refer to APL/W Programming Reference and Manual, and online tutorial for details.
A call-back function, or event procedure, is a defined function associated with any event of GUI objects. A call-back function is associated with an objects event by assigning this objects EVENT property. It can be assigned during the creation of such object or it can be assigned later after the object has already been created. Here is the syntax of the assignment of call-back functions:
('EVENT' 33 'START')
or
('EVENT' 'SELECT' 'START')
or
('EVENT' 'SELECT' 'START' 2)
The first line associates START as the call-back function of event number 33, which is SELECT as well. The second line performs the same as the first line. The third line adds an optional argument 2 to call-back function START. Here is the header line syntax of call-back functions in APL/W environment.
{Return} {Given Optional Arguments} CALL_BACK_FUNCTION {Event_Message}
Note that developers of APL/W decided to implement a different syntax when writing programs to call call-back functions. Each of the following examples of START can serve as a call-back functions.
START
[1] © Dyalog APL/W allows a call-back function without the definition
[2] © of return value and formal event message passed in.
START MSG
[1] © Dyalog APL/W allows a call-back function without the definition
[2] © of return value.
RSTART MSG
[1] © The regular format for a call-back function.
[2] ©
[3] RMSG © Just send the message back to Windows for default processing
MSG{STATUS}START MSG
[1] © Dyalog APL/W allows a call-back function with an optional left argument
[2] ©
[3] R-1 © Inform Windows to do nothing responding to this event
R{STATUS}START MSG
[1] © The return value depends on the existence of left argument.
[2] © If left argument exists, return it to Windows for processing.
[3] © If left argument dose not exist, return event message to Windows for
[4] © processing.
[5] R(1+0¹NC'STATUS')STATUS MSG
Note that if there is no return value defined inside START, APL just lets Windows perform default actions. It works the same way as if the return value is assigned the same value as the event message. The shy return value can also be assigned another event message composed by the programmer, so that Windows can pass back to APL and start another event process loop 6. The shy return value can also be numerical such as 0, 1, or -1, which directs Windows to perform either the default action, or not. Please also refer to APL Programming Manual or online tutorial for more information.
PROMPT, which demonstrates the Execute Expression method and also demonstrates the need for a Call-by-Reference mechanism:
PROMPT
RTITLE PROMPT PRMPT
[1] © a modal dialog box, prompt a message (* given PRMPT *)
[2] © R is 1 if user selects Yes and R is 0 if user selects No.
[3] 'PROMPTMSG'WC'MSGBOX'('Caption'TITLE)('Text'PRMPT)('STYLE' 'QUERY')
('EVENT' 'MSGBTN1' 'R1')('EVENT' 'MSGBTN2' 'R0')
[4] DQ'PROMPTMSG' ª EX'PROMPTMSG'
Please note that this function works as a cover function for the standard APL/W GUI object MsgBox in Query style. Note also that the way this program was written is only to make it more modular. Programmers can simply plug in a line like the following to give the user a prompt and to receive the users reaction, without extra coding:
RESULT'Prompt Test:' PROMPT 'Do you want to save the results?'
This is the modal window when it is run:
The shortcomings of this implementation for the GUI environment is that functions executed inside Execute Expressions do not have the rich information passed in as in event messages for call-back functions. For the purpose to emulate call-back functions to process event, you have to pass the event information into Execute Expressions, the same information as is passed into call-back functions in event messages. Unfortunately, most of the information is simply not available because it is directly reported from Windows GDI interface.
If Call-by-Reference is allowed in APL/W programming, the running program might look like this:
PROMPT
RTITLE PROMPT PRMPT
[1] © a modal dialog box, prompt a message (* given PRMPT *) and provide
[2] © R is 1 if user selects Yes and R is 0 if user selects No.
[3] 'PROMPTMSG'WC'MSGBOX'('Caption'TITLE)('Text'PRMPT)('STYLE' 'QUERY')
('EVENT' 'MSGBTN1' 'QUERY_USER_SELECTION' R)('EVENT' 'MSGBTN2' 'QUER
Y_USER_SELECTION' R)
[4] DQ'PROMPTMSG' ª EX'PROMPTMSG'
Now, R works as value passed back to user action to MsgBox, which is a more consistent and logical way of program coding.
As described above, the Call-by-Reference mechanism allows programmers to write functions or procedures with more flexibility in argument passing. The way a variable is modified in a function or procedure is clearly indicated, as it is shown as an argument passed in either Call-by-Reference or Call-by-Value fashion. It also provides another method to return a value in addition to return values of any procedure. It is most useful when the usage of return values is pre-defined, such as call-backs in the Dyalog APL environment. Actually, the usage of return values is more universally and consistently pre-defined in C programming language. C language assumes every function or procedure is actually a function that returns something every time. Its up to the programmer to determine whether to assign a return value explicitly inside defined functions, and whether to explicitly use the return value or not. Combined with Call-by-Reference mechanism, C programming language provides users with extreme flexibility.
This mechanism also benefits programmers to program in modular style. Since return values are always treated as error codes, it would be much easier to write general purpose modules to fit into different programming tasks. Actual arguments needed by sub-routines and return values needed by calling programs can be handled elegantly by the combination of Call-by-Value and Call-by-Reference mechanisms.
It is an excellent solution to utilize Call-by-Reference mechanisms for call-back functions in Dyalog APL GUI environments. Left arguments passed into call-back functions, provided by programmers as the arguments, can be modified inside call-back functions. After the call-back function exits, arguments would be passed back to calling functions with modified values. Programmers dont have to implement global variables or put modified values inside the DATA property of any objects. If the values passed into call-back functions are not intended to change, just dont change them in call-back functions. Applications with call-back functions written with Call-by-Value only fashion in mind would not need to change at all.
This solution can be expanded further into a formal specification for APL defined functions. The specification states: The left argument of a defined function can be either explicit or shy. If the left argument exists, it is passed into defined function in Call-by-Reference convention. The outcome of such addition in specification is tremendous. It provides with the solutions to the problems described above for call-back functions. It also provides with a great flexibility for programmers in terms of argument passing mechanism. The effort to adopt this new specification into existing APL codes written without Call-by-Reference mechanism is minimum. If your existing programs never change the values of left arguments inside defined functions, you dont have to change any code at all.
There is a requirement when implementing value passing from inside call-back functions back to the function that defines the associated object in Call-by-Reference fashion. Programmers have to issue QuadDQ explicitly after all GUI objects have been created to make sure that the program is not terminated after all GUI objects are given to users to interact 7 with. There are more considerations on this issue and they will be discussed later.
A simple function to increment any variable with given interval. The concept is similar to ++ operator in C language. Here are the code segment and how it works(assuming Call-by-Reference is available):
> {R}VAR INCREMENT INC
[1] © return value R is 0 if operation fails, 1 if succeeds
[2] © set TRAP to check DOMAIN ERROR only as an example
[3] R1 ª TRAP11 'e' '
R0'
[4] VARVAR+INC
And here is how it works:
A3
ERRORA INCREMENT 1
ERROR
1
ERRORA INCREMENT 3
ERROR
1
A'TEST'
ERRORA INCREMENT 1
ERROR
0
A3
A INCREMENT 1
A'TEST'
A INCREMENT 3
In the above test runs, you can notice that it is very easy to perform error checking without bringing up the session window. The value of A is updated after the completion of the program if Call-by-Reference mechanism is used.
A function as a call-back function that changes the state of a running program with pre-defined finite states. The call-back function simply passed back new state based on user actions as the Call-by-Reference argument and program running state are modified. Other functions can reference to this program state easily. A very practical usage of this type of function is to keep track of Graphical Context (or GC in short) in modern GUI programming APIs.
A function uses Call-by-Reference convention of arguments as a place holder for requested information. You click on a button, then the requested information is passed back as an argument you pass into the function. Your running program then has the variable with a good value in it. This concept is very useful in implementing Client/Server architecture for database access. First, the call-back function checks the availability of the server and database, and returns a flag for it. Another call-back function requests total record counts that satisfy some given query criterias based on the result of server availability check. The third function can then request real information from those satisfied records.
Before we put any real efforts into work, some other points are noted here:
LX'MAIN_PROGRAM ª DQ ''.'' ª OFF'
This line directs APL run-time environment to do three things while it starts and loads the workspace. 1). Start MAIN_PROGRAM. 2). Give control to user. 3). After the execution of whole program, log off of APL.
That means it is impossible to return any value into calling functions since calling functions are already finished running in this programming style. Another program structure has to be implemented so that the Call-by-Reference mechanism can be used everywhere.
In a similar situation, where your program is implemented in procedural fashion and some steps in the whole program flow require user interactions with GUI objects, you must issue QuadDQ explicitly right after the creation of GUI objects to give GUI objects to users. Or more precisely stated, you have to issue QuadDQ right after the creation of GUI objects to explicitly force your users to respond to GUI objects before users can proceed to next step. The Portfolio Management System mentioned in the first part of this paper serves as a good example.
The mechanism to pass arguments back from defined functions by the Call-by-Reference calling convention definitely gives a reasonable solution to avoid the problems from not being able to return values in addition to error checking codes. Programmers can avoid the usage of global variables or avoid the need of double error checking by implementing functions in Call-by-Reference fashion. But it also brings up some other issues as noted above. I hope that other APL vendors in addition to Dyadic, such as IBM, could come out with some solutions for argument passing mechanisms. It would be even better if Dyadic and IBM could work together to come out with a specification that can be included in ISO APL specifications. Every vendor could implement this mechanism accordingly. However, upon considering the potential problems we might have, it is still too early to jump to a conclusion yet. Overall, any additional features for programmers to program in APL in terms of flexibility and functionality are always welcomed.
WTUTOR provided by Dyadic, in Event Processing section, Dyalog APL/W performs default processing if 0 is given to a specific event; and it ignores the event if -1 is given to such an event. Obviously these behaviors are quite different from those when invoking UPPER workspace. Any comments from Dyadic?
UPPER will modify event message only if the character is a small cap letter.
This list of publications provide general background reference.
Donnelly, P. [1995]. Dyalog APL/W 7.x User Guide and Windows Interface Reference Manuals, Dyadic Systems Limited.
Aho, A. V. and Sethi, R. and Ullman, J. D. [1986]. Compilers Principles, Techniques, and Tools. Addison-Wesley.
Eklof, M. D. and Mcdonnell, E. [1993] Programming Language APL, extended International Standards Organization. Committee Draft 1.
Marcotty, M. and Ledgard, H. [1986]. Programming Language Landscape Syntax , Semantics, Implementation. Second Edition. SRA.
Vincent Lin
Jennison Associates
Boston, MA