grabbing value of a variabe by its name?

Richard A. O'Keefe ok at cs.otago.ac.nz
Mon Jul 29 03:21:24 UTC 2002


Ragnar Hojland Espinosa <ragnar at linalco.com> is still trying to
defend a maintenance-disaster-type pun.

	If I got to choose which code to maintain, this:
	
	execute 'insert into table1 (firstName,lastName,position,address,zip,country) values (',
	        firstName asQuotedPostgres,',',
	        lastName asQuotedPostgres,',',
	        position asQuotedPostgres,',',
	        address asQuotedPostgres,',',
	        zip asQuotedPostgres,','
	        country asQuotedPostgres,')'.
		"ad nauseum"
				
This is a straw man; it's easy to knock over but there is no real
substance to it.  Nobody in his or her right mind would write code
like this, and I have given several examples in this thead of how
it could be done better.

The starting point is the observation that the data are not magically
born from Zeus's brow already in variables that have exactly the right
names.  The data come from somewhere.  When they are stored, there is not
the slightest reason to put them in method temporaries, and every reason
to put them somewhere else.

To give my example before,

    r := Table1 new.
    r firstName: "wherever the data REALLY come from".
    r lastName: "wherever the data REALLY come from".
    r position: "wherever the data REALLY come from".
    r address: "wherever the data REALLY come from".
    r zip: "wherever the data REALLY come from and what have hot water
            heaters got to do with it anyway?".
    r country: "wherever the data REALLY come from".
    "etc ad nauseam spon".
    r insert.

This is doing things an OBJECT-ORIENTED way.  You want to insert something
into a table.  What do you want to insert?  Variables?  No: you want to
insert an OBJECT.  So let there *BE* an object.

In particular, this carries over to update:

    Table1 do: [:r |
        r country = #NZ ifTrue: [r price: r price * 0.80. r replace]].


	or this:
				
	fields _ SQLFields new: #(firstName lastName position address zip country).
	execute 'insert into table1 (', fields asNames, ') values (', fields asValues, ')'.
		
	I know which I would choose.

Please reconsider.  This is the dark side of Perl.

	Not only its quicker and cleaner,

It is quicker and cleaner than your straw man, but it is NOT quicker or
cleaner than doing things in an object-oriented way.

For an example of the way it's not object-oriented, you MAKE an object,
but then you don't USE it as an object.  Why not

    (SQLFields new: #(firstName lastName position address zip country))
        insertInto: #table1.

	its also safer safer

It is safer than your straw man.  What wouldn't be?  But it is NOT safer
than doing things in an object-oriented way.  For example, suppose we have
a Table1 class, the way I keep suggesting, whose instances represent
past, current, future, actual, or possible rows of 'table1'.  The methods
in that class can
 - provide default values
 - enforce integerity constraints before touching the data base
 - handle version changes
For example, suppose the column 'zip' in your data base is renamed from
the US-centric 'zip' to a more globally intelligible 'postcode'.  ALL of
your versions break, but the Table1 class can just add two methods
    zip	^self postcode
    zip: x  self postcode: x
and your code keeps running.  THIS is what I call safety; depending on
accidental puns is what I call HIGHLY RISKY.

	and encourages a coherent homogeneus naming convention across
	classes for data extracted from the database with a localized "trick"

I completely fail to see how this trick can possibly encourage a
'coherent homogeneus (sic!)' naming convention.  It fails at the first step:
 * It simply isn't *POSSIBLE* to use the same names in your Smalltalk code
   as in your data base.

   Here is a sample table I happen to have kicking around.
    create table Results (
       Round                decimal(2)    unique not null,
->     Home_Team            character(14),
->     Home_Score           decimal(2)    not null,
->     Away_Team            character(14),
->     Away_Score           decimal(2)    not null,
       foreign key (Home_Team) references Teams(Team),
       foreign key (Away_Team) references Teams(Team),
       primary key (Round, Home_Team, Away_Team)
    );

    I suppose you could use #round as a method temporary, but you could
    NOT use #home_team, #home_score, #away_team, or #away_score in Squeak.
    (ANSI Smalltalk, yes.  Squeak, no.)

 * The trick does not particularly encourage consistency.  SQL names are
   not case sensitive.  So homeTeam, homeTEAM would both represent the
   same SQL name.  How exactly does the trick encourage people to consistently
   pick one of them, in a way that gentle exhortation would not?

 * Sometimes in SQL you have to use qualified names.  The example data base
   that my example comes from includes also
    create table Teams (Team ...)
    create table League (Team ...)
   You have not yet explained how your pun can accomodate SQL names like
   Teams.Team or League.Team.

 * Some names which are perfectly usable as SQL names are reserved words
   in Smalltalk, such as "self", "super", "nil".

 * You have not yet explained how your pun can accomodate SQL delimited
   identifiers.  For people who never looked at SQL 92, "foo!" (with the
   double quotes) is a perfectly good SQL 92 'delimited identifier'.
   Delimited identifiers may be used pretty much anywhere that simple
   identifiers may be.

 * According to the SQL 1992 standard, an identifier may contain non-Latin
   letters if such exist according to the <module character set specification>
   or the <character set specification>; how are those to be handled by this
   pun?

But above all, the trick fails at the second step.  Suppose in one method
you need to refer to TWO tables.

    t := Teams new.
    t team: 'Hawkeyes'.
    t insert.
    lg := League new.
    lg team: t team.
    lg wins: 0; losses: 0; draws: 0; forScore: 0; againstScore: 0.
    lg insert.

In this case, the two rows happen to need the same value for their
'team' columns.  But what if they needed DIFFERENT values for their
team columns?

    winners := 'Hawkeyes'.
    losers := 'Windies'.
    w := Wins new.
    w team: winners; against: losers.
    w insert.
    l := Losses new.
    l team: losers; against: winners.
    l insert.

I cannot for the life of me regard any approach which would force
the entry into Wins and the entry into Losses to use the *same* local
variables for opposite purposes as 'safe'.




More information about the Squeak-dev mailing list