Getting started with OOP for User Language programmers
Background
So, you're a User Language programmer and you're thinking about learning object-oriented programming:
- Rumor has it you can be a more effective programmer if you use object-oriented techniques.
- You're tired of feeling inferior to object-oriented programmers because they speak a language that you don't understand but sure as heck sounds impressive.
- You use Sirius Software products and Sirius has announced that all new User Language functionality will used object-oriented syntax.
- “Object-oriented” looks better on your resume than “User Language”.
- You just want to learn something new.
Unfortunately, most User Language programmers' first experience with object-oriented programming is painful and bewildering. Often it comes in the form of a VB.Net or Java class where the terminology flows freely from day one. Even worse:
- Classes often emphasize how one architects an object-oriented application. While this might be a logical way to build an application, it's a daunting way to learn a language — like trying to learn ballroom dancing before you know how to walk.
- Teachers (and books about object-oriented programming) are often enamored with the more sophisticated aspects of object-oriented programming languages, leaving novices in the dust as they're still struggling to digest the simpler concepts.
- Many object-oriented concepts are inter-related so it often requires plowing ahead without fully understanding the concepts one has already learned.
- There seems no way to put the concepts learned in a Java or VB.Net class to use in User Language. So, one is either forced to try to work on object-oriented programming in one's free time, or the concepts are quickly forgotten.
Fortunately, there is a way you can learn object-oriented programming, and apply the principles on-the-job from day one!
One requirement for this is that one work at a site where Janus SOAP User Language Interface is available or, at least some of the other Sirius “API” products such as Janus Web Server or Janus Sockets.
First, a quick word about terminology. In English-speaking, as opposed to American-speaking countries, objected-oriented is usually called object-orientated. More commonly, everyone just calls object-oriented programming “O-O”. But then, you knew that.
Mixed case code
So, let's get started. First, if you're going to do O-O programming you've got to write your code in mixed case. No, there is no technical reason O-O code in User Language must be in mixed case but:
- It's easy enough to do and, even if you don't use O-O, it makes your code look more modern.
- The industry consensus is that descriptive, often compound words, are better for variable, function, and subroutine names than terse non-descriptive names. For example, %itemNumber is better than %ITMN. While the latter is easier to type, the former is much easier to read. USing %ITEMNUMBER, on the other hand, clearly blunts some of that readability benefit. %itemNumber is written in what's called CamelCase.
- Sirius Software depends on CamelCase to make their function and subroutine names readable.
Fortunately, it's easy to start using mixed case code. Simply start typing your code in mixed case. What can possibly go wrong? If you're system manager has set everything up nicely for you, nothing. But, if not, you might hot a few glitches that are easy enough to fix:
- If you use the 204 editor and type in mixed case code and it gets converted to upper case when you hit enter, you have two options:
- Set *LOWER at command level. This is a bit of a hassle because now Model 204 commands require holding the shift key down, because mixed case Model 204 command don't work.
- Set the SIREDIT user parameter to X'33' (only the X'01' bit is required, but you may as well set some others) before entering the editor. This causes Model 204 to essentially switch to *LOWER mode before entering the editor. Request that your system manager do this for everyone by setting SIREDIT X'33' in CCAIN.
- If you get compilation errors when you type in mixed case UL, it means that the compiler is not running in case-independent mode. To fix this, you have three options:
- Have your system manager set the X'01' bit in the system COMPOPT parameter. This must be done in CCAIN and is probably the best/simplest option.
- Change the BEGIN or B statement at the start of the request you're working on to use mixed case, as in Begin, begin, or b.
- If you can't change the start of the program, add the line Sirius Case ToUpper (the case of the words, doesn't matter) to the start of the procedure you're working on.
The following is an example of a mixed case User Language program that starts with a mixed case Begin:
begin print 'Hello World!' end
The following is an example of an Included procedure that contains a Sirius Case ToUpper directive:
sirius case toUpper subroutine hello print 'Hello World!' end subroutine
Note that mixed case UL support is case independent so you can write UL statements in an case, and you can refer to Model 204 variables in any case. The following illustrates a little island of mixed case code in the middle of some upper case code:
SUBROUTINE FOOBAR(%INPUT IS FLOAT) %MSG IS STRING LEN 32 %TOTAL IS FLOAT ... if %input gt %total then %msg = 'Input value too big' end if ... PRINT %MSG END SUBROUTINE
This illustrates that you don't have to convert an entire procedure (or request) to mixed case to take advantage of mixed case UL, though, obviously, in the long-term, it's a good goal to aim for relatively consistent casing in all your code. In the short term, however, a bit of inconsistency will have to be tolerated to get to the point where most or all UL code is in mixed case. Certainly, any new procedures should be written completely in mixed case.
As this example, illustrates, you don't have to learn anything new to enter UL in mixed case (all statements still work the same way) so there is no excuse not to start.
Object-oriented syntax
The most pernicious difference between O-O languages and procedural languages such as User Language (we'll just call it UL from here on) is the syntax. And the biggest syntactic difference between O-O and UL is how functions and subroutines are invoked. Let's start with functions. All UL programmers know how to invoke a function.
First, functions are called $functions (dollar-functions) or £functions (pound-functions) in the UK. $functions (except some of the Sirius $functions) always return a value so must either be on the right side of an assignment, input to a subroutine or other $function call, or inside some UL expression. The following example, has a $substr in all three contexts:
%x = $substr(%y, 3, 10) call clever($substr(%y, %start, %len)) %z = $substr(%y, %len, 1) + 10
As the above example illustrates, and all UL programmers know, a $function can be followed by the $function arguments (inputs) in parentheses with multiple arguments separated by commas.
O-O functions, on the other hand, use a syntax where a function invocation consists of the thing (object, if you will) that the function is operating on specified before the function name, followed by its arguments inside parentheses. Many $functions have O-O equivalents and $substr is no exception: its O-O equivalent is called substring. The following illustrates the use of the O-O substring function by replacing $substr in the previous example with substring:
%x = %y:substring(3, 10) call clever(%y:substring(%start, %len)) %z = %y:substring(%len, 1) + 10
While this might look strange to a User Language programmer, it uses the most common O-O syntax so can honestly be called O-O programming. So, if you find any $substr call in your system and change it to use substring (moving the first argument before a :substring) you have now done some O-O coding. It's that easy!
To further add to your bona fides as an object oriented programmer, don't call substring a function, but call it a method. In addition, don't call %y just a string, call it a string object. Now you're ready for something more advanced. Say “I applied the substring method to the string object”. Repeat until it feels natural to say it. Congratulations, you're well on your way to becoming an O-O programmer and, maybe even a guru. If you're curious about what you just said:
- Method is just a fancy word that means function or subroutine or any other called code that does something.
- Object is just a fancy word for a thingy.
Now, of course, there are many functions available other than just Substring. The functions that operate on strings are called Intrinsic String Methods. You can find a list of the methods at List of Intrinsic String Methods. There are also functions that operate on numbers. The list of these can be found at List of Intrinsic Float Methods.
Now, while congratulating yourself on your new-found skills, you might have the gnawing feeling inside that you really haven't accomplished a lot, as much as you have learned. So what have you accomplished? Is O-O syntax really better than what you're used to? At first blush, it would seem worse, as the traditional $function call is more like English, where verb (function name) precedes the object (first parameter), as opposed to the O-O syntax, where the order is reversed. For example, one would say “get the substring of %y” not “%y get the substring” so
%x = $substr(%y, 2, 3)
seems more natural than
%x = %y:substring(2, 3)
While this might be true, it's easy enough to get used to the second form — in many languages the object comes before the verb.
In any case, the chief advantage of O-O syntax is that because the input of a function is to the left of the function, one can invoke a function and then pass the result of that function to another function by placing the second function call after the first. Similarly, a third function can be placed to the right of second to process its output. To illustrate, consider the following:
%x = %y:substring(%start, %len):toUpper:unspace
This reads rather nicely, left to right: take %y get a substring, convert to upper case, and remove extra blanks. The traditional version doesn't read nearly as nicely:
%x = $unspace($upcase($substr(%y, %start, %len)))
Since the processing happens in inside out order, presumably one should read this code inside out but, of course, this is very difficult. In addition, because only a colon is required as a separator character, and because no dollar-sign is required to distinguish a function from other entities, the O-O version is shorter, in spite of the fact that the function names are somewhat longer, and so, more meaningful. Perhaps more important, the O-O expression contains fewer “noise” characters. All of these lead to much better readability for the O-O expression. And, just as spaces can be placed around parentheses in a $function invocation to improve readability, spaces can be placed around the colon used to separate the object and function name:
%x = %y : substring( %start, %len ): toUpper :unspace
This example used inconsistent spacing just to illustrate what's allowed, not to suggest that inconsistent spacing is recommended — it's not.
So, to continue the process of becoming an object-oriented User Language programmer, you should familiarize yourself with the list of intrinsic string and float methods and try to use them wherever possible and try to use them in lieu of the $function equivalents. There is no performance penalty for doing so — in some cases O-O functions and $functions share the same underlying code.
Named parameters
The Sirius object-oriented User Language implementation has support for named parameters, parameters that can be specified by name, rather than position. While, strictly speaking, this has nothing to do with O-O programming (in fact, few O-O languages support it -- Java and VB.Net do not) named parameters are used in many Sirius methods, so it is important to understand them. To specify a value for a named parameter, simply specify the name, followed by an equals sign (=), followed by the value. For example, the Right and Left String functions have a Pad named parameter that indicates the pad character to be used if the input string is shorter than the requested length:
%x = %y:right(20, pad='0')
In this case, the name simply makes the code clearer as without the name:
%x = %y:right(20, '0')
it's far less obvious what the second parameter means. In functions with large numbers of parameters, the named parameters can also be very useful for eliminating the need for placeholder commas, and for making the function invocations more readable. String and Float methods tend not to have a lot of parameters so named parameters are not heavily used for them, but they are used here and there, so it's important to understand them.
Stringlists
The object-oriented extensions to User Language are provided by Sirius Software and are only available at sites that can use $lists. Most sites that can use $lists, do use $lists because many Sirius $functions require them as input or outputs, and because they are just generally useful. $lists are essentially objects because an object is a container for information that is accessed via a reference variable, and multiple reference variables can refer to the same object ($list). For example:
%list is float %list2 is float ... %list = $listNew %list2 = %list $listAdd(%list, 'Now is the winter') print $listInf(%list2, 1)
In this example, the Print statement would end up printing “Now is the winter” even though the $listAdd added the line to %list. Both %list and %list2 ;point to the same $list (object) and, so, it is not surprising that what is added via %list can be seen via %list2. It is clear, too, that %list and %list2 must be pointers to $list objects and cannot be the objects, themselves, since a Float value couldn't possibly hold the contents of a $list. An experienced UL programmer might raise an eyebrow at the line that contains the $listAdd in the above example. $listAdd is a function and, so, returns a value. But the $listAdd is not on the right side of an assignment. What gives? Well, the result of many $functions is usually “uninteresting” to the UL code so it seems silly to have to find a variable to which to assign it. So, the Sirius Mods allows certain $functions to be invoked without assigning its result to anything. This causes the $function to behave more like a subroutine as subroutines are called without obtaining a return value from the subroutine. It is this blurring of the distinction between functions and subroutines that leads to the O-O term method which is a convenient way of referring to either.
So, when you were using $lists, you were using object-oriented programming capabilities even though you might not have realized it. However, you were not using object-oriented syntax to access the $list objects and so the benefits of a pure object-oriented facility were not available to you. Now, you might just say “Hold on, didn't you say strings are also objects?&rdquo. Indeed they are. However, they're objects that can't be changed once they're set (so-called immutable objects) — you can't really change a string that's been assigned to a variable, you can only assign a new string to that variable. Most objects are not immutable though, of course, strings and numbers are heavily used, so have importance disproportionate to their number. Nevertheless, the thing that distinguishes O-O languages from non-O-O languages is the presence of changeable (non-immutable) objects so $lists can be viewed as a step toward object-oriented programming.
The pure object-oriented equivalennt of $lists are called Stringlists. And, using a Stringlist is very similar to using a $list. To illustrate, let's take the above example and convert it to using Stringlists:
%list is object stringlist %list2 is object stringlist ... %list = new %list2 = %list %list:add('Now is the winter') print %list2:item(1)
Your first reaction might be an understandable “So what?” Well, let's take a look at what we've accomplished. First, we've made it much clearer what's in %list and %list2: references to Stringlists. This prevents accidents such as someone accidentally incrementing the value of a %list — the following would not be allowed:
%list is object stringlist
...
%list = %list + 1 <==Invalid
Nor, would the following:
%list is object stringlist
%list2 is float
...
%list = %list2 <==Invalid
While this might not seem that useful, it can be much more useful in complex applications with lots of different kinds of objects — except for strings and numbers, only object variables of the same class can be assigned to each other. “Wait a minute, what's a class?” you say. A class is simply a name for objects with identical characteristics. In this case, all Stringlists have a similar structure and have a fixed set of things you can do with them (like $lists). The distinction between a class and individual objects is the same as in normal usage — my blue Subaru is an object in the class of cars.
In any case, beyond the protection of accidental assignment mismatches, what else has using an O-O Stringlist instead of a traditional $list bought us? Well, compare:
$listAdd(%list, 'Now is the winter')
with
%list:add('Now is the winter')
In the former, when the UL compiler hits the $listAdd it has no idea that it's working with a $list. So, to distinguish $listAdd from other functions that might be doing an add of something to something, the word list has to be used as a qualifier in the $function name — it would not do to have the $function called simply $add. But, with the O-O syntax, the compiler first hits the %list and so now “knows” that whatever follows must be something specific to a Stringlist. So, it is sufficient to use the function name Add because it can only mean the Add that applies to Stringlists, not to other classes. This means that
- The O-O statements tend to be shorter, because the extra qualifiers on the function names are unnecessary. This is often true even when the O-O statements use longer, more meaningful function names than the non-O-O equivalents.
- The absence of the extra qualifiers eliminates a lot of the “noise” in O-O statements. Since $lists names usually have the word “list” in them to keep them straight from other variables and since all the $list functions start with “$list”, code the manipulates $lists often turns into the actual functional code swimming in a sea of the word “list”.
Consider, for example, code that subsets, sorts, and then prints the contents of a $list:
$list_print($listSort($listSub(%list, 'foo'), '1,10,A')))
Now, consider the O-O equivalent:
%list:subset('foo'):sortNew('1,10,A'):print
Not only is the O-O version considerably shorter, it is much easier to follow — again, it can be read left to right. In the $function version, our eyes glaze over at the sea of $lists and nested parentheses. While, this is an extreme example, it can safely be said that code using O-O syntax is almost always tidier and easier to read than the traditional non-O-O syntax.