Morfik CSharp Subroutines and Scope

Some additions were made to the C# language in order to fully support the underlying Morfik language semantics which is that of a hybrid object-procedural language, since C# does not have the concept of a stand-alone subroutine, being it a purely object oriented language. Morfik C# is a hybrid language syntax, adding support for non-class entities.


Some additions were made to the C# language in order to fully support the underlying Morfik language semantics which is that of a hybrid object-procedural language, since C# does not have the concept of a stand-alone subroutine, being it a purely object oriented language. Morfik C# is a hybrid language syntax, adding support for non-class entities.

Types of Subroutines

In Morfik C# there is only one fundamental type of subroutines: Functions. Subroutines that have no return type are clearly represented as a different kind of entity in other languages such as Basic or Pascal but is treated just as a special case in Morfik C#, just as C# treats methods.

Functions

Functions are subroutines that do return values. Because of this, functions can be used within expressions where you would normally use a literal value.

The following is a short example of a function:

  String GetTestString{
    Return "Test!";
  }

Returning Values

Returning values from a function can be done in Three different ways: through the Return command, Result variable or by assigning a value to the name of the function itself.

In the previous example you can see the use of the return command which takes the following form:

  Return <value>;

You can assign the value you want to return from a function to the function’s name, as if it was a variable. This takes the following form:

  FunctionName = <value>;

If you have chosen to use this method, you cannot read back the value assigned to the function name. This would be interpreted by the compiler as actually being a recursive call to the function. In order to get around this limitation, common practice is to create an intermediate variable which will hold the value until such time as you are ready to exit the function, keeping the value available for reading. In order to simplify and standardize the usage of this pattern, Morfik C# uses the Result variable. Assigning a value to the Result variable is essentially the same as assigning it to the name of the function but at any time you can use Result in the right side of an expression, without initiating a recursive call. Usage of the Result variable takes the following form:

  Result = <value>;

Void Functions

Void functions are subroutines that, by definition, do not return values, (except via input parameters passed by reference, which is explained later). Since they do not return a value, Void functions cannot be part of expressions.

The following is a short example of a Void function:

  Void Test(){
    File Of Integer F2;
    AssignFile(F1, "c:\test.int");
    Rewrite(F1);
    Write(F1, 9);
    CloseFile(F1);
  }

Parameters

Passing parameters to subroutines is done through a list which is constructed in a very specific way.

Creating a Parameter List

A subroutine parameter list is very similar to a sequence of variable declarations. This can be observed in the following example.

  Void Test(String Param1, Integer Param2);

This simple example shows a parameter list with two parameters of different types. Morfik C# has a number of modifiers which can be applied to parameters to change how they are handled by the compiler. The following is a sample of all possible forms of declaring parameters.

  Void Test(      Integer  pValueParameter1, 
                  Const Integer pConstParameter, 
                  Ref   Integer pVarParameter, 
                  Out   Integer pOutParameter, 
                  Const pConstUntypedParamter, 
                  Ref   pVarUntypedParameter, 
                  Out   pOutUntypedParameter
                 )
  {
  }

There are basically five different kinds of parameters:

1. By Value

2. Const

3. By Reference

4. Output only

5. Untyped parameter

Parameters can be categorized in the following way:

  • Any parameter which does not have a Ref, Const or Out modifier can be considered a parameter passed by value.
  • Any parameter which has no type definition can be considered as an untyped parameter.

Parameters Passed By Value

A parameter passed by value acts as a variable local to the subroutine to which it belongs. The parameter is initialized to the value passed to the subroutine and though it can be altered inside the routine, the new value will not persist after the code exits the subroutine.

  Void ABC(Integer Param1) {
    Param1 = Param1 * 2;
  }

If no modifier is specified for a parameter it is presumed by the Morfik C# compiler to be a parameter passed by value.

  {
    Integer I;
 
    I = 2;
    ABC(I);
    CallOtherProc(I);
  }

The value of variable "I" when the call to CallOtherProc is reached will be 2. The value of the original variable has not been altered since the ABC routine worked on a copy of the value held by the original value By value parameters can receive the result of any expression that yields a compatible value as can be seen in the following example.

  ABC ( (3+6) * 3-1 );

Const Parameters

Const parameters (constant parameters) are very similar in usage to value parameters but they cannot be altered within the subroutine. The main purpose of constant parameters is to serve as insurance to the developer that when a parameter is not meant to be altered, it is not.

Note: In Morfik 07 the Morfik C# compiler will not immediately complain if you try to alter a Const parameter within a subroutine. When you do a full compile of the project, however, the backend platform compiler will complain.

Parameters Passed By Reference

Parameters passed by reference, using the Ref modifier, should be used whenever a subroutine needs to return a modified value for one or more of its parameters. By reference parameters can only be called with the use of variables since any literal expression is evaluated to a value and would not be addressable in memory to have its content changed.

Usage of by reference parameter within a subroutine is exactly like that of a by value parameter, except that the value of the variable which was passed as a parameter will be altered once the execution of the routine concludes. The following is an example of passing a parameter by reference:

  Void ABC(Ref Integer Param1){
    Param1 = Param1 * 2;
  }

In this case if we applied the same code that we used to review the workings of by value parameters we would have a different result.

  {
    Integer I;
 
    I = 2;
    ABC(I);
    CallOtherProc(I);
  }

The value of variable "I" when the call to CallOtherProc is made will actually be 4, since inside the subroutine the value of 2 was multiplied by two and that result was returned in the parameter declared with the Ref modifier.

Output-Only Parameters

Output-only parameters are specifically for returning more than one value from a subroutine. For returning a single value the norm is to just use a function. When there is a need to return more than one value from a subroutine, multiple output-only parameters should be used. Output-only parameters are declared using the Out modifier. Any value the subroutine receives in an Out parameter is discarded.

Untyped Parameters

When a parameter does not have a predefined data type it can receive any variable or constant, it cannot receive a literal value, though, as untyped parameters can only be declared as Ref, Const or Out. Morfik C# does not allow for By Value un-typed parameters.

Untyped parameters are passed as memory addresses and it is up to the developer to know how to handle them within the subroutine.

Note: An untyped parameter can be used as a way of being able to pass parameters of different types to a subroutine. In many situations the same results can be achieved by using different overloaded versions of a Function. Though there are situations where you could not accommodate all the necessary data types with overloaded functions, their usage offers the added benefit of additional type checking.
For more information see Overloading Subroutines later in this chapter.

External Subroutines

External subroutines can be brought into your Morfik C# universe from the Browser or the intermediate code level on the server side of your application. External subroutines are declared with the assistance of compiler metadata attributes. The following is an example how a server side external routine can be imported into your code:

  String IntToStr(Integer AnInteger); ["external=systemext"];

JavaScript Subroutines

Sometimes, if you are experienced in using JavaScript, you might want to do some experimenting with JavaScript code in your Morfik application. It is possible to include inline JavaScript code on the browser side of your application through the use of JavaScript subroutines. The following is an example of how to create an inline JavaScript subroutine in your application.

Javascript Void Go() 
/*!
alert('programming in javascript is fun!');
*/

Notice that the body of JavaScript code is actually contained within a multi-line comment, within your Morfik C# code. The JavaScript modifier in the subroutine’s declaration instructs the compiler to look for the comment containing the JavaScript language code, right after the subroutine’s header.

You can also use parameters with your inline JavaScript subroutines. In the next example you can see the same routine from the previous example, but using a parameter for the string which is shown, instead of a string literal.


Javascript Void Go(String mymessage) 
/*!
Alert(mymessage);
*/
Note: The inline JavaScript code is not checked by the Morfik C# compiler so it is entirely up to the developer to make sure that the code is correct or runtime errors might occur.

Overloading Subroutines

In Morfik C# it is possible to have several different subroutines with the same name, as long as they have different parameter lists. This is called Overloading and can be really useful. In Morfik C# all you need to do in order to overload subroutines is to declare more than one subroutine with the same name. The following is an example of how to use subroutine overloading.

  String toString(Integer AnInt) 
  {
    Return IntToStr(AnInt);
  }
 
  String toString(Double AFloat)
  {
    Return FloatToStr(AFloat);
  }
Note: The addition of Overloading capabilities to the underlying Morfik semantic structure, which is the basis for Morfik C#, was done at the end of the product development cycle, for Morfik 07. This means that this feature is not exploited to its full potential by the Morfik Framework itself.
In future releases of Morfik you will, probably, see this feature being more widely used.

Nested Subroutines and Data Types

Morfik C# fully supports nesting subroutines within one another. You can also have nested data types, i.e. data types which are declared within a subroutine. The following example shows how the syntax for these two features is implemented in Morfik C#.

  Void Test1(Integer Param1)
  {
      //Nested data type declaration
      typedef integer* PInteger;
 
      //Nested subroutine
      Void Test2(String Param2){
      ShowMessage(Param2);
      }
 
      Pinteger P;
      P = &Param1;
      Test2(IntToStr(*P));
  }

Scope Rules

A reference to an identifier, within an application, must happen within that identifier’s declaration scope. The following are the main scope definition rules for Morfik C#.

Subroutine (Function) Scope

Subroutine scope is defined by the boundaries of the subroutine itself. Any variables, data types, and nested subroutines it declares will be visible only from within that subroutine. A nested subroutine will have access to any identifier declared prior to its declaration, within the surrounding subroutine.

A nested subroutine can define an identifier with the same name of an identifier declared in the subroutine in which it is nested. The innermost variable will then hide the outermost one.

Class Scope

An identifier declared within a class is visible to all members of the class and its descendants. This scope included instance variables, methods and properties. The Subroutine Scope rules apply to all method declarations. If the identifier is declared as private, it will be visible within the class but not to its descendants.

Module Scope

Identifiers (variables, data types, subroutines, classes) declared with the private modifier are only visible within the module in which they are declared. They will be visible to any subroutines and classes declared within the same module.

Public Scope

Identifiers (variables, data types, subroutines, classes), declared with the public modifier, are visible within the module in which they are declared and in any module that imports that module. They will be visible to any subroutines and classes declared within the same module and within the modules that have imported the module in which they are declared.

This is the broadest scope level in Morfik C#.

In Listing 1 you can see examples of all the possible scope levels in Morfik C#.

Listing 1 – Possible Scopes in Morfik C#

Namespace MyModule  // Module/Public Scope
{
 
 /**
  MyInt is available within this module and in any module 
  that imports this module, since it is declared as public. 
  (Public Scope)
*/
  Public Integer MyInt;
 
  Public Class AClass { // Class Scope
    /**
      Class level MyInt is available only within this class since 
      it is declared private.  Within the class it hides the module
      scope MyInt
    */
    Private Integer MyInt;
 
    /**
       Method OuterFunc defines a new scope
    */
    Public String OuterFunc{  //Method Scope
    /**
     Method level MyInt is available only within this method.
     Within the method it hides the Module and Class 
     scope variables of the same name.
   */
    Integer MyInt;
 
      String NestedFunc { //Nested Function Scope
        /**
          Nested function level MyInt is available only within this
          function.  Within the function it hides the Module, Class
          and method scope variables of the same name.
        */
        Integer  MyInt;
 
        //Body of the nested function
      }
 
    }//Body of the outer function
  }  // Class definition
 
/**
  GlobalProc is available within this module and in any module 
  that imports this module because it is declared as public..
*/
Public Void GlobalProc{  //Function Scope
 /**
  function level MyInt is available only within this function
  Within the Procedure it hides the Module scope variable of 
  the same name.   (Public Scope)
*/
  Integer MyInt;
}
 
  /**
   AnotherInt is available only within this module since it
   is declared as private. (Module Scope)
  */
Private Integer AnotherInt;
 
/**
  InternalProc is available only within this module since it 
  is declared as private. (Module Scope)
*/
  Private Void InternalProc{  // Function/Procedure Scope
  }
 
}

Wrapping it up

Morfik C# offers a powerful array of features from which to build your application. These include extensive support for subroutines including overloading and nesting.


Related Topics

See Also

Back to top