Updated: 9/5/2002
In debates with OO fans, one often finds that criticisms directed toward procedural/relational (p/r) paradigm(s) involve flaws or limitations with specific languages, most notably the often awkward C (apologies to C fans). These are usually language-specific criticisms and not inherent flaws in p/r itself. Thus, they attempt to throw out the baby with the bath water.
Another complaint is that p/r results in the usage of excessive global variables. An actual look at cases where global variables are used often reveals situations in which additional p/r language constructs could have been used instead. (In some cases it is nothing more than bad design on the programmer's part.)
This document describes some of the constructs which make p/r more competitive with object oriented languages and other paradigms.
Run-time evaluation can be found in many languages in the form of an Eval function.
x = "print('Hello World')" eval(x) // result: "Hello World" // Observer pattern usage observeUs = "foo; bar; yaz; taz" // routine names for each i in split(observeUs, ";") eval(i & params) // append std. param list endForThere are also more advanced forms of run-time evaluation, but an Eval-like operation is usually sufficient. I have even kicked around the idea of having entire routines stored in tables. This could make finding and cross-referencing routines much easier. Why build all this into an IDE when RDBMS have most of it built-in already?
Note that it is probably best to use run-time expressions only in cases where programmers or technical operators maintain the table, and not in cases where application users maintain tables (which would usually be via a UI). For example, a programmer may manage a data dictionary, but not a corporate department list.
Another situation is implementing generic string and variable melding, such as implementing a Perl-like dollar sign variable substitution in strings. See the dollar sign implementation example
Inheriting scope can also reduce the need for global variables for variables that are shared by a parent routine and children.
There are at least 3 approaches to subroutine nesting. The first is physical nesting, as found in Pascal. The second is linking by naming. A subroutine can declare itself is being the child of another routine. The 3rd approach is for a subroutine to declare itself as inheriting any scope from it's caller (except for variables explicitly declared as being local in the child). The 3rd approach allow more genericy than the other two approaches in my opinion because it can be called from any routine. (However, it reduces ability to do compile-time checking and optimization.)
In some languages, such as Pascal, the name of the nested routine is only recognized within the parent routine. This can reduce routine naming conflicts, but requires more complex syntax for the parent to distinguish between same-named children routines and outside routines. (Some sort of packaging scheme perhaps would be preferable to solve this issue.)
They are also useful for adding parameters without changing every existing subroutine call. (Some languages are picky about having every call have the same number of positional parameters.)
invline("Subtotal", subtotal, #color red) invline("Tax", tax) invline("Shipping", shipAmt, #color green #italics) invline("Total", tot, #italics #bold #color yellow #font_color black)In this consumer invoice line example (roughly based on L syntax) the # symbol marks a named parameter. Note how some parameters do not need sub-parameters. For example, "#bold" stands by itself. Some forms of named parameters would instead require something like "bold=true".
Implementing the above routine with positional parameters would require at least 6 positions. It would be hard to remember which attribute was associated with which position. As a general rule of thumb, named parameters are a likely candidate if a routine needs more than 4 or 5 parameters, especially if only some of them are required or infrequently used.
An alternative is using a dictionary array (also known as an associative array).
var x[] x.title = "Shipping" // same as x['title']="Shipping" x.value = shipAmt x.italics = true x.color = "green" writeIt(x) // or x.writeIt()This approach resembles OOP syntax. Note that "x.writeIt()" is equivalent to "eval(x.writeIt)" in L syntax. The syntax of associative arrays differs per language.
rs = OpenRecordSet(.....) rs.setField("zipcode", "12345") // lousy setField(rs, "zipcode","12345) // still lousy rs.zipcode = "12345" // better x = rs.getField("zipcode") // lousy x = getField(rs,"zipcode") // still lousy x = rs("zipcode") // slightly lousy x = rs.zipcode // betterNote that the "dot syntax" is not isolated to OOP languages. In some languages it represents built-in table syntax, in others it is an associative array (also known as a "dictionary").
Although I disagree with heavy usage of dictionaries for data handling, they make wonderful interface packaging mechanisms. For example, I had an application that has a bunch of message handling global variables that dealt with the timing, polling, and handling of message files. If the language had dictionary arrays, I could put all these variables into a dictionary to give them a kind of grouping, but without the bloat and syntax overhead of a dedicated class.
The syntax for dictionaries varies, but one approach is to allow the "dot" syntax if there are no embedded spaces or odd characters in the index. (In such cases, use something like brackets instead). In some ways a dictionary acts a lot like an instance variable syntactically.
var bob[] // declare dictionary bob["shoe_size"] = 9 // traditional syntax bob["age"] = 40 bob.age = 40 // dot syntax bob.shoe_size = 9 // dot syntax print bob.shoe_size // result: 9Sometimes one can even put program code into dictionaries to get a class-like feel (at least in an OO scripting sense, for fans of strong-typing would not like it). This triggers interesting chicken-egg debates about whether OO classes are really dictionaries or dictionaries are really classes. (Your answer probably depends upon which you like the most.) A reserved dictionary entry, something like "~sys_parent", can even be used to create dictionary attribute inheritance in theory.
The biggest syntactical complaint is the annoying "break" statement found in C and Java and their derivatives. Not only are break statements extra syntax, but if you forget to include one, you can have some hard-to-find bugs. This alone would probably be enough to drive me to OOP.
Fortunately, other languages are not so stupid. (Some say C did it this way so that multiple case blocks could optionally be executed, but IF statements are better for that sort of thing.)
In the Inheritance section of the Shape example you can see a more rational case statement example. (It barrows from Visual Basic. Note that this is not a general endorsement of VB. VB has it's own set of flaws.)
We can do this with table-level views, but would have to rename any reference to our original table in any existing code to get it. We just want to add a (virtual) column, not a new table in into the table name-space.
Although Data Dictionaries can do some of this, a better place is the base data scheme. (Data dictionaries may include calculated fields, virtual fields, and other information that may not be one-to-one with actual fields.)
This applies to tables as well as fields. For example, a long description of each table, and the primary key (if applicable) can be included.
In my opinion SQL is far from ideal. For one, it relies too much on nested clauses when referenced (by-name) clauses would be preferable for non-trivial query statements. Reference-based would allow application query designers to design ad-hoc (temporary) view-like constructs, for example, to simplify or partition the query text into easier-to-relate-to sections or clauses. Large SQL statements can be like trying to figure out a paragraph-sized run-on sentence on a university reading exam. (Gee, I hope my web-pages don't have any of those.)
For more on SQL and vendor criticism and possible alternatives, see SQL Criticism, Dynamic Relational and SQL Alternatives.