Project Methods

4D - Documentation   Français   English   German   4th Dimension 2004, Command Theme List   4th Dimension 2004, Command Alphabetical List   4th Dimension 2004, Constant Theme List   Back   Previous   Next

version 6.0


Project methods are aptly named. Whereas form and object methods are bound to forms and objects, a project method is available anywhere; it is not specifically attached to any particular object of the database. A project method can have one of the following roles, depending on how it is executed and used:

Menu method

Subroutine and function

Process method

Event catching method

Error catching method

These terms do not distinguish project methods by what they are, but by what they do.

A menu method is a project method called from a custom menu. It directs the flow of your application. The menu method takes control—branching where needed, presenting forms, generating reports, and generally managing your database.

The subroutine is a project method that can be thought of as a servant. It performs those tasks that other methods request it to perform. A function is a subroutine that returns a value to the method that called it.

A process method is a project method that is called when a process is started. The process lasts only as long as the process method continues to execute. For more information about processes, see the section Processes. Note that a menu method attached to a menu command whose property Start a New Process is selected, is also the process method for the newly started process.

An event catching method runs in a separate process as the process method for catching events. Usually, you let 4D do most of the event handling for you. For example, during data entry, 4D detects keystrokes and clicks, then calls the correct object and form methods so you can respond appropriately to the events from within these methods. In other circumstances, you may want to handle events directly. For example, if you run a lengthy operation (such as For...End For loop browsing records), you may want to be able to interrupt the operation by typing Ctrl-Period (Windows) or Cmd-Period (Macintosh). In this case, you should use an event catching method to do so. For more information, see the description of the command ON EVENT CALL.

An error catching method is an interrupt-based project method. Each time an error or an exception occurs, it executes within the process in which it was installed. For more information, see the description of the command ON ERR CALL.

Menu Methods


A menu method is invoked in the Custom Menus environment when you select the custom menu command to which it is attached. You assign the method to the menu command using the Menu editor. The menu executes when the menu command is chosen. This process is one of the major aspects of customizing a database. By creating custom menus with menu methods that perform specific actions, you personalize your database. Refer to the 4th Dimension Design Reference manual for more information about the Menu editor.

Custom menu commands can cause one or more activities to take place. For example, a menu command for entering records might call a method that performs two tasks: displaying the appropriate input form, and calling the ADD RECORD command until the user cancels the data entry activity.

Automating sequences of activities is a very powerful capability of the programming language. Using custom menus, you can automate task sequences that would otherwise be carried out manually in the User environment. With custom menus, you provide more guidance to users of the database.

Subroutines


When you create a project method, it becomes part of the language of the database in which you create it. You can then call the project method in the same way that you call 4th Dimension's built-in commands. A project method used in this way is called a subroutine.

You use subroutines to:

Reduce repetitive coding

Clarify your methods

Facilitate changes to your methods

Modularize your code

For example, let's say you have a database of customers. As you customize the database, you find that there are some tasks that you perform repeatedly, such as finding a customer and modifying his or her record. The code to do this might look like this:

      ` Look for a customer
   QUERY BY EXAMPLE([Customers])
      ` Select the input form
   INPUT FORM([Customers];"Data Entry")
      ` Modify the customer's record
   MODIFY RECORD([Customers])

If you do not use subroutines, you will have to write the code each time you want to modify a customer's record. If there are ten places in your custom database where you need to do this, you will have to write the code ten times. If you use subroutines, you will only have to write it once. This is the first advantage of subroutines—to reduce the amount of code.

If the previously described code was a method called MODIFY CUSTOMER, you would execute it simply by using the name of the method in another method. For example, to modify a customer's record and then print the record, you would write this method:

   MODIFY CUSTOMER
   PRINT SELECTION([Customers])

This capability simplifies your methods dramatically. In the example, you do not need to know how the MODIFY CUSTOMER method works, just what it does. This is the second reason for using subroutines—to clarify your methods. In this way, your methods become extensions to the 4th Dimension language.

If you need to change your method of finding customers in this example database, you will need to change only one method, not ten. This is the next reason to use subroutines—to facilitate changes to your methods.

Using subroutines, you make your code modular. This simply means dividing your code into modules (subroutines), each of which performs a logical task. Consider the following code from a checking account database:

   FIND CLEARED CHECKS   ` Find the cleared checks
   RECONCILE ACCOUNT   ` Reconcile the account
   PRINT CHECK BOOK REPORT   ` Print a checkbook report

Even for someone who doesn't know the database, it is clear what this code does. It is not necessary to examine each subroutine. Each subroutine might be many lines long and perform some complex operations, but here it is only important that it performs its task.

We recommend that you divide your code into logical tasks, or modules, whenever possible.

Passing Parameters to Methods


You'll often find that you need to pass data to your methods. This is easily done with parameters.

Parameters (or arguments) are pieces of data that a method needs in order to perform its task. The terms parameter and argument are used interchangeably throughout this manual. Parameters are also passed to built-in 4th Dimension commands. In this example, the string "Hello" is an argument to the ALERT command:

   ALERT("Hello")

Parameters are passed to methods in the same way. For example, if a method named DO SOMETHING accepted three parameters, a call to the method might look like this:

   DO SOMETHING(WithThis;AndThat;ThisWay)

The parameters are separated by semicolons (;).

In the subroutine (the method that is called), the value of each parameter is automatically copied into sequentially numbered local variables: $1, $2, $3, and so on. The numbering of the local variables represents the order of the parameters.

The local variables/parameters are not the actual fields, variables, or expressions passed by the calling method; they only contain the values that have been passed.

Within the subroutine, you can use the parameters $1, $2... in the same way you would use any other local variable.

Since they are local variables, they are available only within the subroutine and are cleared at the end of the subroutine. For this reason, a subroutine cannot change the value of the actual fields or variables passed as parameters at the calling method level. For example:

      ` Here is some code from the method MY METHOD
      ` ...
   DO SOMETHING ([People]Last Name) ` Let's say [People]Last Name is equal to "williams"
   ALERT([People]Last Name)

      ` Here is the code of the method DO SOMETHING
   $1:=Uppercase($1)
   ALERT($1)

The alert box displayed by DO SOMETHING will read "WILLIAMS" and the alert box displayed by MY METHOD will read "williams". The method locally changed the value of the parameter $1, but this does not affect the value of the field [People]Last Name passed as parameter by the method MY METHOD.

There are two ways to make the method DO SOMETHING change the value of the field:

1. Rather than passing the field to the method, you pass a pointer to it, so you would write:

      ` Here is some code from the method MY METHOD
      ` ...
   DO SOMETHING (->[People]Last Name) ` Let's say [People]Last Name is equal to "williams"
   ALERT([People]Last Name)

      ` Here the code of the method DO SOMETHING
   $1->:=Uppercase($1->)
   ALERT($1->)

Here the parameter is not the field, but a pointer to it. Therefore, within the DO SOMETHING method, $1 is no longer the value of the field but a pointer to the field. The object referenced by $1 ($1-> in the code above) is the actual field. Consequently, changing the referenced object goes beyond the scope of the subroutine, and the actual field is affected. In this example, both alert boxes will read "WILLIAMS".

For more information about Pointers, see the section Pointers.

2. Rather than having the method DO SOMETHING "doing something," you can rewrite the method so it returns a value. Thus you would write:

      ` Here is some code from the method MY METHOD
      ` ...
   [People]Last Name:=DO SOMETHING ([People]Last Name) ` Let's say [People]Last Name is equal to "williams"
   ALERT([People]Last Name)
      ` Here the code of the method DO SOMETHING
   $0:=$1
   ALERT($0)

This second technique of returning a value by a subroutine is called "using a function." This is described in the next paragraphs.

Advanced note: Parameters within the subroutine are accessible through the local variables $1, $2... In addition, parameters can be optional and can be referred to using the syntax ${...}. For more information on parameters, see the description of the command Count parameters.

Functions: Project Methods that return a value


Data can be returned from methods. A method that returns a value is called a function.

4D or 4D Plug-in commands that return a value are also called functions.

For example, the following line is a statement that uses the built-in function, Length, to return the length of a string. The statement puts the value returned by Length in a variable called MyLength. Here is the statement:

   MyLength:=Length("How did I get here?")

Any subroutine can return a value. The value to be returned is put into the local variable $0.

For example, the following function, called Uppercase4, returns a string with the first four characters of the string passed to it in uppercase:

   $0:=Uppercase(Substring($1; 1; 4))+Substring($1; 5)

The following is an example that uses the Uppercase4 function:

   NewPhrase:=Uppercase4 ("This is good.")

In this example, the variable NewPhrase gets "THIS is good."

The function result, $0, is a local variable within the subroutine. It can be used as such within the subroutine. For example, in the previous DO SOMETHING example, $0 was first assigned the value of $1, then used as parameter to the ALERT command. Within the subroutine, you can use $0 in the same way you would use any other local variable. It is 4D that returns the value of $0 (as it is when the subroutine ends) to the called method.

Recursive Project Methods


Project methods can call themselves. For example:

The method A may call the method B which may call A, so A will call B again and so on.

A method can call itself.

This is called recursion. The 4D language fully supports recursion.

Here is an example. Let's say you have a [Friends and Relatives] table composed of this extremely simplified set of fields:

- [Friends and Relatives]Name

- [Friends and Relatives]ChildrensName

For this example, we assume the values in the fields are unique (there are no two persons with the same name). Given a name, you want to build the sentence "A friend of mine, John who is the child of Paul who is the child of Jane who is the child of Robert who is the child of Eleanor, does this for a living!":

1. You can build the sentence in this way:

   $vsName:=Request("Enter the name:";"John")
   If (OK=1)
      QUERY([Friends and Relatives];[Friends and Relatives]Name=$vsName)
      If (Records in selection([Friends and Relatives])>0)
         $vtTheWholeStory:="A friend of mine, "+$vsName
         Repeat 
            QUERY([Friends and Relatives];[Friends and Relatives]ChildrensName=$vsName)
            $vlQueryResult:=Records in selection([Friends and Relatives])
            If ($vlQueryResult>0)
               $vtTheWholeStory:=$vtTheWholeStory+" who is the child of "+[Friends and Relatives]Name
               $vsName:=[Friends and Relatives]Name
            End if 
         Until ($vlQueryResult=0)
         $vtTheWholeStory:=$vtTheWholeStory+", does this for a living!"
         ALERT($vtTheWholeStory)
      End if 
   End if 

2. You can also build it this way:

   $vsName:=Request("Enter the name:";"John")
   If (OK=1)
      QUERY([Friends and Relatives];[Friends and Relatives]Name=$vsName)
      If (Records in selection([Friends and Relatives])>0)
         ALERT("A friend of mine, "+Genealogy of ($vsName)+", does this for a living!")
      End if 
   End if 

with the recursive function Genealogy of listed here:

      ` Genealogy of project method
      ` Genealogy of ( String ) -> Text
      ` Genealogy of ( Name ) -> Part of sentence

   $0:=$1
   QUERY([Friends and Relatives];[Friends and Relatives]ChildrensName=$1)
   If (Records in selection([Friends and Relatives])>0)
      $0:=$0+" who is the child of "+Genealogy of ([Friends and Relatives]Name)
   End if 

Note the Genealogy of method which calls itself.

The first way is an iterative algorithm. The second way is a recursive algorithm.

When implementing code for cases like the previous example, it is important to note that you can always write methods using iteration or recursion. Typically, recursion provides more concise, readable, and maintainable code, but using it is not mandatory.

Some typical uses of recursion in 4D are:

Treating records within tables that relate to each other in the same way as in the example.

Browsing documents and folders on your disk, using the commands FOLDER LIST and DOCUMENT LIST. A folder may contain folders and documents, the subfolders can themselves contain folders and documents, and so on.

Important: Recursive calls should always end at some point. In the example, the method Genealogy of stops calling itself when the query returns no records. Without this condition test, the method would call itself indefinitely; eventually, 4D would return a "Stack Full" error becuase it would no longer have space to "pile up" the calls (as well as parameters and local variables used in the method).

See Also

Control Flow, Database Methods, Methods.


4D - Documentation   Français   English   German   4th Dimension 2004, Command Theme List   4th Dimension 2004, Command Alphabetical List   4th Dimension 2004, Constant Theme List   Back   Previous   Next