[REBOL] Re: Tools for Rebol Coders
From: joel:neely:fedex at: 6-Jan-2002 8:20
Hi, Sunanda,
Minor quibble, and other thoughts...
[SunandaDH--aol--com] wrote:
...
> So my serious suggestion is for lint.r named after the
> traditional C program that tells you just how many rules
> you've possibly broken. A Rebol Lint checker could for
> example highlight possible problems in this code:
>
> myFunc: func [Offset Size]
> [
> MyLayout: layout [button "hello"] Offset
> View/New MyLayout "My Layout"
> ]
>
> -- Function header doesn't specify datatypes
> -- "Size" defined in function header but not used
> -- "Mylayout" created as global
Setting global "MyLayout" (MyLayout might have already
existed, rather than being
created, and it's entirely possible -- style arguments
aside -- to write one or more functions which initialize
global data.)
> -- "Offset" in Layout evaluated by not assigned
> -- "My Layout" string in View/New evaluated by not assigned
> -- No explicit Return statement
>
The last one gets closer to my puzzlement over how to write
any sort of linting, prettyprinting, etc. for REBOL, given
the dynamic nature of the language. I probably won't say
this as concisely as I'd like (I don't have time to use
fewer words.)
In a "fall-through" exit, the value of a function is the
value of the last expression evaluated. This is A Good
Thing, because I can write:
delta: func [a [number!] b [number!]]
[ either a < b [b - a] [a - b]]
without cluttering up the desired expressions with a bunch
of RETURN calls that add nothing to the function.
Hmmmm... I think to myself, "Why not a warning if the
last expression returns an UNSET value?" For example:
printdelta: func [a [number!] b [number!]]
[ either a < b [print b - a] [print a - b]]
This raises some issues:
1) Maybe I wanted to define a function that is evaluated
solely for its (side-)effect and not for a value.
This is not uncommon. In languages that give a return type
in the function prototype it's easy to distinguish between
void foo (...) {...}
and
int foo (...) {...}
to know what the programmer may have intended. We don't
have any such hint in REBOL.
2) There are other ways to write the above function in
conventional REBOL style, including some that nest
expressions quite deeply, as in:
printdelta: func [a [number!] b [number!]]
[ print either a < b [b - a] [a - b]]
The unique "expressional" style of REBOL leads me to think
that a lint-like program would have to be capable of full-
blown expression (including data type) analysis to be able
to determine the type of the last expression (or even to
*find* the last expression!)
3) The previous issue becomes even more pronounced when
one considers the use of (first-class!) functions as
arguments to other functions:
foodelta: func [foo [function!] a [number!] b [number!]]
[ foo either a < b [b - a] [a - b]]
where one must understand something of the behavior of *each*
FOO to know whether there's a return-type inconsistency,
which implies that different uses of FOODELTA might need to
get different warning messages depending on the argument of
the moment...
4) REBOL is a dynamic language which only constructs
function values by actually evaluating code. Given the
ability to construct the blocks that are passed to FUNC,
MAKE OBJECT!, etc... one may have no idea from the static
text what will happen at run time.
The only escape I see from all of this is to have a version
of REBOL that requires the result type(s) of functions to
be specified in the prototypes as well, and specifies the
type signatures of functional arguments (and returned values)
also -- a non-trivial change IMHO.
-jn-
--
; sub REBOL {}; sub head ($) {@_[0]}
REBOL []
# despam: func [e] [replace replace/all e ":" "." "#" "@"]
; sub despam {my ($e) = @_; $e =~ tr/:#/.@/; return "\n$e"}
print head reverse despam "moc:xedef#yleen:leoj" ;