Re: basic Q: Only one way to make vars live outside of the scope of a function without globals?



Hi,

"Ken Fine" <kenfine@xxxxxxxxxxxxxxxx> wrote in message
news:d51m0g$o92$1@xxxxxxxxxxxxxxxxxxxxxxxxxx
> Great answer, Joe, thank you so very much for your thoughtful reply.
>
> Could I ask one other favor of you? You mention a system/convention of
> prefixing that describes the scope of the variable and other useful
> attributes to know. Can you show a sample of this convention, or maybe
> recommend a book that describes the convention that you use? I'm very
> interested.
>
> I've been on a six-month tear of reading through the best comp sci/comp
> engineering literature I can find, and it's pretty cool to see all of the
> tips, tricks and philosophies I've digested translated into markedly
> better
> code. Your system sounds like something worth internalizing.
>
> -KF
>
>
> "Joe Earnest" <jearnest3-SPAM@xxxxxxxxxxxxx> wrote in message
> news:uKp1D%23eTFHA.3344@xxxxxxxxxxxxxxxxxxxxxxx
>> Hi,
>>
>> <kenfine@xxxxxxxxxxxxxxxx> wrote in message
>> news:eGg2QRdTFHA.616@xxxxxxxxxxxxxxxxxxxxxxx
>> > This is a basic question about the design and intent of functions in
>> > programming languages like VBscript.
>> >
>> > I know how to write VBScript functions and to pass parameter variables
> in
>> > and out of them.
>> >
>> > As I've become a smarter programmer, I'm inclined to translate the
>> > lousy
>> > code that I wrote when I didn't know what was doing into more
> granularized
>> > and encapsulated functions and/or classes.
>> >
>> > I was revisiting some VBScript browser detection code that looked
>> > something
>> > like this:
>> >
>> > ' check something
>> > ' set a variable based on result of check
>> > ' check something else
>> > ' set a different variable based on result of check
>> > ' check something else
>> > ' set a different variable based on result of check
>> >
>> > The code sets about a dozen different variables of interest. The code
> was
>> > not organized as a function. It's certainly easy enough to make it a
>> > sub
>> > or
>> > function and to call it, but the variables that are set internally in
> the
>> > function don't live outside of the scope of the function.
>> >
>> > I want someone to confirm that the "correct" way/only way to make many
>> > variables survive outside of the function is to return an object (e.g.
> an
>> > array) of values upon the function's completion.
>> >
>> > The function could write global vars, but that isn't good practice.
>> >
>> > Am I correct in this, or is there another way that I'm missing?
>> >
>> > Thank you,
>> > Ken Fine
>>
>> For my two cents worth ...
>>
>> You missed a thread a couple of weeks ago where Al Dunbar (one of the
>> MVPs
>> here) and I had a lengthy "discussion" over a slightly more complex
> version
>> of your question.
>>
>> Given the straightforward nature of your question, I believe that the
> answer
>> is clearly "no," and I believe that most VBS scripters would agree,
>> though
>> perhaps in different ways.
>>
>> An array return (in scripting, "object" usually refers to a COM object
>> instance and a specific data subtype) is great for similar multiple
>> items.
>> But it is the most obscure return, since you don't have the benefit of
>> the
>> variable name to provide quick identification. And at times you may want
> to
>> return quite different values -- both strings and object instances, or
>> different types of references -- from a single function.
>>
>> VBS (unlike JS and some other languages) maintains the ByRef/ByVal
> argument
>> distinction, and defaults to ByRef. ByRef arguments provide for returns
>> directly to the calling script, without having to use global variables to
>> achieve the return. Indeed, there is no functional difference between
>> the
>> transitory function-name return and a ByRef argument variable return.
> ByRef
>> arguments have been the traditional method in BASIC programming, since
> early
>> DOS days, to get multiple return items from a subprocedure.
>>
>> mainNumRtn= myFunction(useValue1, useValue2, rtnObjVar, rtnStrVar)
>>
>> WMI functions are replete with return arguments -- indeed the WMI
>> registry
>> access system only works that way.
>>
>> The "trick", if you will, in using ByRef argument returns is either to
>> document the function or to use a scope-oriented and functional variable
>> prefixing system (instead of a simple variable-type system, which may not
> be
>> too useful in a pure variant language such as VBS). I strongly prefer
> real
>> prefixing. I can tell by looking at the first character of the argument
>> variable name that I assign whether it's passed ByVal or ByRef, and if
>> ByRef, whether its preserved, destroyed, coerced to the data type and
> range
>> required by the function, requires precise assignment, or set and
> returned.
>> The VBS option to use ByRef arguments is very efficient. But to take
>> advantage of it, you must be willing to coerce or destroy some argument
>> values, as well as reset some for return. This requires comment or a
>> meaningful prefixing system, both to preserve your sanity and for reuse
>> of
>> the function in future scripts.
>>
>> A ByRef return argument for a function can be analogized to a property
>> return for a method function. If your writing classes, you should
> consider
>> associated property returns. Since most I now write my fundamental
>> functions as WSC VBS, I again use global variables for multiple return
>> values, though these are declared in the parent XML script as properties
> and
>> returned to the calling script as properties. As you note, it is
> generally
>> better not to mix scope and use global variables for multiple return
> values,
>> in straight script, since it makes it hard to reuse the functions that
>> you
>> write.
>>
>> Regards,
>> Joe Earnest

First off, my apologies for the omitted phrases and bad grammer in the last
response. It had been a long day.

Prefixing systems are the type of thing that can create flame posts --
people seem to take them very personally. My system has developed through
DOS to Windows and ASM to scripting. My current system, however, is focused
on VBS, and even underwent a bit of a change a couple of years ago, when I
switched largely to WSCs from straightforward scripting. There are a number
of factors to consider in choosing a prefixing system that may make my
approach unsuitable for you. Probably the two most significant ones are:

1) The peer group where you work or with whom you exchange or mutually work
on code, or a potential successor for a client, or even a publication, may
expect or require a prefixing system that everyone can recognize quickly;
and

2) If you are working in multiple platforms and languages, and particularly
if those multiple platforms or languages are involved in the same code or
project, you may need to adapt your prefixing system to a
least-common-denominator approach that addresses the requirements and quirks
of all the platforms and languages involved.

Also, before I give you my prefixing system, you might want to take a look
at Eric Lippert's blog on type prefixing in VBS. Eric Lippert wrote a
substantial amount of the VBS code for MS.

http://blogs.msdn.com/ericlippert/archive/2003/09/12/52989.aspx

A bit of history that may be useful when reading Eric's blog, or other
people's comments (from Alex Angelopoulos [MVP]):

"Suggested naming conventions abound, most variants of Hungarian Notation
(HN), a standard for variable naming developed at Microsoft in the early
1980's for use in workgroup-based C coding. The real story is not this
convention but the concepts researched by it's father, Charles Simonyi. In
graduate research at Xerox PARC he extensively investigated group-based
coding. He focused heavily on how code can be improved by use of certain
shared conventions. To this day one of the clearer benefits of any naming
standard is ensuring that when you look at code you quickly can see what a
variable represents and what a function does."

Let me say at the ouset that I claim no "divine insight" in my prefixing
system, or that it would work for anyone else. It is not even strictly
logical, but has simply proven to be more useful to me than a strictly
logical system. It will likely not be intuitively recognized by other
scripters. It is optimized for VBS and even for WSC-hosted VBS, with a
secondary focus on mark-up languages (HTML, XML, etc.), so its
cross-platform and cross-language application is questionable.

Regards,
Joe Earnest

-----
Modified mark-up language "camel" casing is employed for all user-defined
names; external objects, properties and methods; and constants. The initial
name segment is fully lowercased, while the initial character of
non-delimited secondary name segments is capitalized. All inherent VBS
keywords and function names fully lowercased. Entities and names created
through XML and HTML methods are named as if they were created through VBS
methods.

All procedures are declared as functions, even if they provide no return and
are designed to be called only as statements. Unexposed functions are
prefixed with an initial "fn" segment. Exposed methods and properties, and
their corresponding functions, are not prefixed, unless a conflict with an
inherent VBS name requires the internal function to be prefixed. Unexposed
components (loosely, the WSC file equivalent of classes) are designated with
an initial "cp" segment, while exposed components are named to provide a
recognizable call.

All script is "Option Explicit". Variables are declared when and where
required. The VBS "Const" statement is never used. The underscore
character is not used in variable names, unless use is compelled by context,
but is, instead, reserved solely for line continuations.

All variable names are prefixed, except for object variables that access
unexposed components. This allows the variable name to be used as an
exposed property.

My peculiar variable prefixing is oriented toward identifying scope and
usage, as well as identifying data subtype, when useful. Variables are
given concise name segments to differentiate them and generally identify
their content in context. Pluralized names are used for totals, as well as
for transitory variables that may change subtypes between arrays and string
lists. For example, in a hypothetical procedure, "viFile" could be a local
counter or iteration of files, "viFiles" could be a local reference to the
total number of files, "vaFiles(...)" could be a local array of file
objects, "vsFileName" would be a local file name string, and "vsFileNames"
would be a local string list of file names that could be changed back and
forth, to and from, an array, using the VBS "Split" and "Join" functions.

Prefixing involves two characters: (1) the first indicates scope and
argument usage, or is omitted for global variables; (2) the second indicates
type and usage, to the extent that I have found it useful.

Variable Scope Prefix

none e.g., sFile
Globally defined.

v e.g., vsFile
Locally defined or ByVal argument. Both local definitions and ByVal
argument variable values go out of scope when the procedure is exited. All
ByVal arguments are internally coerced to the procedure's required data
subtype and/or range, but, because it is ByVal, the value passed is
preserved. Note that, in VBS, object instances are always passed ByRef,
even if there is a ByVal argument statement.

r e.g., rsFile
ByRef argument returned. The value passed is modified in the procedure for
return to the calling script. If an initial value is required, the data
passed will be internally coerced to the required data subtype and/or range.

u e.g., usFile
ByRef argument unchecked. Data for the argument must be passed as the
anticipated subtype and/or range, to avoid an error in the procedure. The
value passed is preserved.

p e.g., psFile
ByRef argument preserved. The data passed is coerced to the anticipated
subtype and/or range without destruction, so that the value passed is
preserved.

c e.g., csFile
ByRef argument coerced and destroyed. The data passed is coerced to the
anticipated subtype and/or range, destroying the data passed.

Variable Type and Usage Prefix

The final variable prefix character indicates the variable's type and/or
usage. All categories include an initial "Empty" data subtype, as well as
any data subtype passed through a function's argument, or returned from a
function, method or property, until the anticipated data type can be
coerced. These categories have developed historically and idiosyncratically
over time, and have simply proved useful to me. They don't necessarily make
sense.

a e.g., aFile, vaAmount
Array. A variable that is declared and maintained solely as an array.
Transitory string-list arrays may have an "s" prefix. Transitory
string-lists that convert to arrays with mixed data types may have an "x"
prefix. Note that this prefix does not articulate the data subtype(s) or
any other aspects of its elements.

f e.g., fErr, vfExists
Boolean or numeric flag. A Boolean subtype, or a (long) integer numeric
subtype with a value of "0" or "-1".

i e.g., iRep, viItem, viItems, viInStrChr, viBit
Integer or long integer. A (long) integer subtype used in a uniquely
"integer" manner, as an instance, counter, pointer, address, element number,
position, bitwise, sequential or other numeric code, etc., or, with a plural
name, the total instances or the UBound of an array.

n e.g., nHt, vnAmount
A numeric subtype. Any other numeric data subtype or numeric usage.

s e.g., sFile, vsName, vsFileList
String. Any string data subtype or usage. May also include transitory
string lists that are changed back and forth to or from arrays.

o e.g., oFso, voShell, uoWshShell
Object. An object instance data subtype or usage. Because VBS requires
that object variables be passed ByRef, all object variables passed to a
user-defined procedure should have a prefix indicating ByRef status.
Scriptlet object variables that are exposed as properties in order to allow
access to unexposed components, are not prefixed, so that the variable name
may be used as an exposed property name.

d e.g., dFileDate, vdToday
Date. A date-time data subtype or usage.

k e.g., kVersion, kSomeBitFlag
Constant. Data of any subtype that is intended to be fixed and unchanged,
once determined. Rarely used, but almost always global in scope, when used.
The VBS "Const" statement is not used, as such.

x e.g., xUtility, vxArgCode, vxValueList
Other, transitory or multi-type. A data subtype such as "Null" or "Error",
a transitory utility variable, a transitory string list that is converted to
or from a mixed data-type array, or an argument that may be validly assigned
different data subtypes.


.



Relevant Pages


Loading