I'm splitting out something Douglas said in another thread today.

> On Sun, Jan 8, 2017 at 1:37 PM, Douglas von Roeder <[email protected]>
wrote:
>
> Unless I'm writing a routine that's drop dead never ever going to be more
> than a utility routine, every method starts with c_longint($h_;$1). That's
> in my default method header, in fact. In many situations, I pass 0 at $1
> but, overall, having set up 4D to write accessor methods for me I've
> shifted my perspective to design code from the perspective of how will the
> data be used so my code tends to be littered with _Set and _Get methods.

I'm glad that you've brought this up as it's another super useful question
to think about. Namely, how to you pass data between methods - the
nitty-gritty. I've got a different point of view than you, but I'm
revisiting my habits and am willing to take a look. (Hopefully, you'll feel
similarly inspired.)

Okay, first for my thoughts on the background. Once upon a time there was
COBAL and Fortran, and there was spaghetti. One of, if not the first, major
coding religion was Structured Programming, which arose in response to the
nightmare that comes from spaghetti code. These guys came up with a bunch
of edicts, largely about managing the passing of data between routines and
(even more important), the passing of control between routines. Off the top
of my head, some of what they were after included:

* Methods that do one thing, and one thing only, which is clearly expressed
in its name.

* All data is passed via parameters, not globals.

* No parameter should be passed solely for the use of an indirect routine.
So, you can't pass along something that is not directly used by the routine
that you're calling. The parameter list needs to be clean, pure, and
directly consumed by the routine that you call. Of course, that routine may
also pass inputs on to subroutines, but you're not supposed to load up the
parameter list with extra values.

* Code should always do one thing, so none of that Case of stuff that we
see in so many 4D programs. With one exception: You can have a 'dispatcher'
routine that does just that, dispatch routines to other routines. Kind of a
special case. The idea is that the routine routes code but doesn't itself
perform work. 4D's On Web Connection is potentially a good example.

* A method should have a single point of entry and exit. (The whole "GOTO
Considered Harmful" thing.)

They also had some weird charts and a bunch of now-incomprehensible stuff
about left-handed-paths or something or other. Much of it sounds completely
archaic and bizarre now, much of it sounds like stuff that "everyone
knows." To be overly reductionist, Structured Programming is all about how
to make parameter lists. How does any of this apply to 4D? Because 4D comes
from Pascal and Pascal was very much about Structured Programming. And if
you look at some of the fundamental features missing from the 4D language,
they're likely because of Structured Programming principles. Example: Why
don't we have Try/Catch and proper exception handling? I don't really know,
but there just isn't a sensible way to implement it without what amounts to
GOTO. Exception handling is a *helpful* consequence of GOTO.

So what has that go to do with anything? Well, computer languages are
developed by people...people with points of view. They tend to care a whole
lot about these little things. I mean, Bertrand Meyer ("Object-Oriented
Construction and Design") has developed an entire language to implement
certain principles. (And to make several categories of bugs literally
impossible to write.) Note that Meyer cites Edsger W. Dijkstra, the "GOTO
Considered Harmful" guy, in the front of OOCD. OOCD is still one of the
most cited book in the history of computer science, which is saying a lot.
That's where programming by contract and a bunch of other concepts come
from.

Anyway, too much history. The point is that massing data and control
through a program is a *fundamental* programming issue. It's an element of
core language design itself, not just our programs. Take that to mean that

1) It's super important.
2) Choices and trade-offs are being made.
3) Some approaches are better at avoiding certain problems than others.
4) It's worth thinking about.

Why bring this up now? Well, because I'm a few years late to the
conversation ;-) Douglas is talking about OT objects, but 4D has had
C_OBJECT since V14. With C_OBJECT you can do something like Douglas and
package up all of your values into an object and pass it along. Prior to
V14, you can do the same with a BLOB and so on. There are some pretty big
pros and cons with this approach. I've got a few cases where I like this
strategy. For example, that's a really good way to pass messages through
any sort of messaging pipeline. The pipeline knows nothing about the
message except how to deliver it. The sender knows how to package the data
and the receiver knows how to unpack the data. Tidy. Otherwise? I've got
pretty mixed feelings. For the example, I'll use an imaginary data type of
C_BUNDLE to stand for the parameter-block-in-a-sock thingy. C_OBJECT, a
BLOB, OT, etc.

Standard 4D code:

ShowRepeatedAlert (Current date;"Hello world";5)

That's pretty easy to read because the parameters are explicit. What about
with a bundle and one parameter?

C_BUNDLE($bundle)
SetDateToBundle($bundle;"Date";Current date)
SetTextToBundle($bundle;"AlertText";"Hello world")
SetLongintToBundle($bundle;"TimesToRepeat";5)

ShowRepeatedAlert($bundle)

This code is easier to follow because the names are set explicitly. Nice.
On the other hand, in a real 4D implementation you can often end up losing
the advantages of the compiler doing type checking, which is a total
bummer. I've written the pseudo-routine-names to try and avoid that problem.

So let's go inside of ShowRepeatedAlert:

C_BUNDLE($1)
C_DATE($date)
C_TEXT($text)
C_LONGINT($count)

// Error checking and input drama here.

$date:=GetDataFromBundle($bundle;"Date")
$text:=GetDataFromBundle($bundle;"AlertText")
$count:=GetLongintFromBundle($bundle;"TimesToRepeat")

So far, so good. I'd say that this is pretty defensible code, even if it's
not everyone's cup of tea. You do have to look at more than the actual call
to figure out what's going on, but hopefully you only have to look right
above the call to see the assignments. At that point, you're happy because
the items are named. As another plus, you no longer have to worry about
parameter order. Switch around the sequence of assignments into the bundle
and it makes no difference to your routine.

So, why am I not always a fan? Well, largely because of another missing
feature from 4D: A way to declare abstract types. By an "abstract type", I
mean a type that you define, that has rules, and that the compiler can
check. (In a way, OO objects can be thought of as an advanced concept of
type. They're more than that, but they're at least that, to be sure.) With
a single bundle of data, all the compiler knows is that something came in.
If you care about validating your inputs, you need to add a lot of
knowledge to your routines to validate the inputs or else write a layer to
perform validation generically. That requires a way of specifying and
validating object elements and their contents. Things get more complicated
in a hurry.

Another drawback to bundles is that they are fundamentally opaque. This
doesn't have to be a problem, but it quickly can be. I was doing a lot of
JavaScript in 2016 and, man, there is some super hard to follow code out
there. JS supports parameters, but it seems unfashionable to use them. You
find these routines that take these ginormous objects with literally
hundreds of attributes. It's completely insane. One missing comma and your
toast, there's no way to debug it sensible...pretty horribly. This is an
extreme (but shockingly common) case, but it's a risk. There's nothing
inherently "object oriented" about stuff a bunch of stuff in a garbage
sack, passing it to another routine, and letting the other routine root
around inside like a Raccoon after a ripe persimmon skin. It's just crappy
coding. (JS offers a very nice alternative: Create a closure with functions
as setters.) In 4D, where how I see this drawback manifest itself most
obviously is when a method gets a bundle, manipulates it and then feeds the
bundle to another routine. Things can get pretty hard to follow in a big
hurry.

So, yeah, there are pros and cons and it's worth thinking about how you
want to go. And, as with so many things, the One True Approach varies,
depending on circumstances ;-)

Stop me before I rant again!
**********************************************************************
4D Internet Users Group (4D iNUG)
FAQ:  http://lists.4d.com/faqnug.html
Archive:  http://lists.4d.com/archives.html
Options: http://lists.4d.com/mailman/options/4d_tech
Unsub:  mailto:[email protected]
**********************************************************************

Reply via email to