Anyone who has used PL/1 is familiar with what they call "pseudo-functions", which in modern terms are what we would now call objects with assignment-operator semantics. PL/1 calls them pseudo-variables because they can be assigned-to the same way as conventional variables can. For example, Basic has a MID$ function, which corresponds to a PL/1 SUBSTR. Examples:
PL/1:
DECLARE (ONE,TWO) CHAR(4);
SUBSTR(ONE,1,2) = SUBSTR(TWO,3,2);
Basic:
DIM ONE, TWO AS STRING
MID$ (ONE,1,2) = MID$(TWO,3,2)
ThinBasic calls the MID$ on the left side a "MID$ Statement", since there is no other grammatical role that could be given to it. This is all well and good as far as MID$ is concerned, but in language terms, it is "cheating". That is, this technique works great for MID$, and for the few other functions that have an alternative "statement" form to them, but it doesn't help me if I want to make my OWN "statement-like" functions. I just can't do it. ThinBasic creates this capability, but reserves it only for itself, not allowing users to write their own such syntax.
I believe it would be of benefit to allow this technique to be applied on a general basis.
In order to do that, a new procedural construct would have to be introduced. Along with SUB procedures and FUNCTION procedures, there would be TARGET procedures.
In some ways, a TARGET is like a "setter" function, a concept sometimes used in languages that support "accessors" or "attribute functions".
One possible use of a FUNCTION/TARGET pair is to define access to an array with bounds that do not start with 1. Another use might be to define "mapping" or "associative storage" functions which store data based on keys. The Dictionary module implements such logic, but targets would allow the use of Dictionaries in a much more natural notation.
A TARGET would have the following characteristics:
- A target is a SUB-like block of code, begun with TARGET and ending with END TARGET
- A target accepts a parameter list like a SUB procedure does
- A target has an AS type-name clause, like a FUNCTION does. However, for a TARGET, the AS type-name specifies the expected type of value to be assigned to the target. For example, a SUBSTR-like target would have an AS STRING clause, since it would be expected that only strings would be assigned to the target.
- A target may have the same name as a FUNCTION. Thus, the same program may have both a FUNCTION ABC and a TARGET ABC.
- When both a FUNCTION and a TARGET exist with the same name, the implementation code for these two procedures are completely different and independent. It is up to the developer to decide how the two procedures relate to each other.
- A target is only recognized as being referenced when it appears on the left side of an = sign
- Inside the code of a target, a built-in function would be required to access the value on the right-hand side of the = sign. This built-in function could have any appropriate name, such as RHS, VALUE, SOURCE, etc. The exact name isn't as critical as the fact that this function is needed. For sake of discussion, let's say this function is called RHS.
- In a TARGET, the RHS function is polymorphic, in the sense that its type is always the same as the AS type-name clause on the TARGET header statement. For a given TARGET procedure, RHS has only one type, and so is not the same thing as a VARIANT. There is no need to define multiple spellings, such as RHS, RHS$, etc. because the type of RHS is known by the AS type-name clause.
- If a TARGET had knowledge of compound assignment operators, such as +=, it would open up a number of intriguing possibilities. However, it would also make the semantics more complex, perhaps prohibitively so. For that reason, this proposal does not define semantics for implementing compound assignment operators, except in the most simple way. That is, if ABC is both a function and a target taking one numeric parameter, then ABC(5) += 1 would be interpreted as ABC(5) = ABC(5) + 1, where the ABC on the left side of the = (and the left of the original += ) would be a reference to the TARGET, and the right-hand ABC is a reference to the FUNCTION of the same name.
Simple (though not terribly useful) example: Define access to a zero-based array:
DIM __MYHEX(16) AS STRING = "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"
FUNCTION MYHEX (N AS LONG) AS STRING
IF N >= 0 AND N <= 15 THEN RETURN __MYHEX(N+1)
RETURN ""
END FUNCTION
TARGET MYHEX (N AS LONG) AS STRING
IF N >= 0 AND N <= 15 THEN __MYHEX(N+1) = RHS
' error handler could be placed here for N out of range
END TARGET
DIM I AS LONG
FOR I = &H0A TO &H0F
' in next statement, left-hand MYHEX is a target, while right-hand MYHEX is a function:
MYHEX(I) = LCASE$ (MYHEX (I))
NEXT
Bookmarks