[REBOL] REBOL Zen / idioms
From: jeff::rebol::net at: 3-Mar-2001 3:09
There's a certain zen to REBOL and it takes time to
understand-- something that I am always learning.
REBOL has a lot of what might seem like idioms, which really
are ways to do things that make life a lot easier. They
stem from the craftsmanship in REBOL, the rather lengthy
effort that went into its design, implementation and polish.
Many problems you face in programming have a nicely crafted
solution sitting inside this little binary interpreter, just
waiting for you to discover it when you need it.
The code that comes from REBOL should demonstrate that Zen
because we all are lucky in that we can pick Carl's brains
over the challenges we encounter. Also, we're lucky because
we get to write REBOL code as an occupation, so we get to
find all those little crafted edges inside. I've always
hoped to pass on what little I have learned of the Zen of
REBOL to any who would walk in that path.
Here is an example of some REBOL Zen style idioms.
============================================
Consider a block of similar objects:
block: [make object! [name: "foo" phone: #222-1111 ... ] ... ]
Now you're writing a function and you have this block and it
is really long with many of the same kinds of objects, but
you need to see if there is an object which has the name
field set to "Waldo" and you need to see if the waldo
object's phone field is set to none, If you don't find this
object you want to do somehing. If you do find waldo and
waldo has a phone you want to call waldo, otherwise you want
to complain that he doesn't have a phone.
Some people might code it like this:
find-waldo: func [block [block!] /local found waldo no-fone?][
found: false
foreach obj block [
if obj/name = "waldo" [
waldo: obj
found: true
no-fone?: not none? obj/phone
]
]
if not found [wheres-waldo?]
either no-fone? [waldo-has-no-phone][call-waldo waldo/phone]
]
That's a fair approach, similar to how you might tackle the
problem in basic, maybe. But with REBOL you can get much
more done in place. Most things return meaningful values so
the left side of most functions represent an excellent
place to dock another useful function, save space, save
steps, and preserve the utility of results.
How about this:
find-waldo: func [block [block!] /local result][
if not result: foreach obj block [
if obj/name = "waldo" [break/return any [obj/phone yes]]
][wheres-waldo?]
either issue? result [call-waldo result][waldo-has-no-phone]
]
Okay, so we have less local variables, the code is smaller
and therefore is more efficient usually (and in this case
definitely). The first example trudged through the whole
block before deciding the outcome, where above we
BREAK/return as soon as we find waldo. The FOREACH will
return the last evaluation, so if FOREACH makes it through
the whole block with out ever finding waldo it will return a
NONE from the last evalutation of the IF statement. We use
the result of FOREACH to immediately determine if we found
waldo. Now if result is not a NONE, it will be either an
issue!, waldo's phone number (#222-111-3333), or a TRUE
value (yes). The TRUE is arbitrary since we just have to
return a non false / non none from BREAK/return and we only
check if we have an issue which means it must be Waldo's
phone number.
Programmers will have different styles, but the language
also has a style of its own.
With REBOL it usually comes down to taking a function,
writing it, then rewriting it a few more times, carving out
the fat while expanding the capability. As a general rule
most REBOL functions that are written can be reworked to
accomplish more, to provide more use in the same amount of
space or less. Code in the eye of the fly and seek to know
the true path of the REBOL way! :-)
-jeff