Tools for Rebol Coders
[1/14] from: sunandadh::aol::com at: 6-Jan-2002 4:59
Hi Carl,
Carl Read in [REBOL] Re: source code layout question
> No, in that I would think it's probably impossible with REBOL. For
> instance, how would you optomize 'blk to allow for a later...
>
> change blk whatever
>
> Your "code" from REBOL's point of view is all just data, remember.
True indeed. I was half joking because I know an optimizer would be of
similar difficulty to writing a compiler, given that anything can change into
anything at anytime.
But only half joking. If the other Carl can make his "mental reduction" then
there is no reason why a program can't at least make similar suggestions,
even if they are wring in a wider context.
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
-- "Offset" in Layout evaluated by not assigned
-- "My Layout" string in View/New evaluated by not assigned
-- No explicit Return statement
Which may immediately lead me to the code I meant to write:
MyFunc: func [Offset [Pair!] Size [Pair!] /local MyLayout]
[
MyLayout: layout/Size/Offset [button "hello"] Size Offset
View/New/Title MyLayout "My Layout"
Return True
]
Of course, with Lint checkers you have to ignore 90% of what they tell you.
But they can spot bugs that the human eye just won't see, so that other 10%
can be lifesaver.
So I am serious about Rebol having some heavyweight tools:
Pretty-print.r with its own dialect for layouting out Rebol code because we
will never get the world to agree on a single style. And even if we did, I'd
still want my code cleaned up at the end of a messy edit session.
Lint.r because an expert eye looking over code is always a useful thing. And
it could help newcomers to the language get up to speed. Again it would need
its own dialect to say what sort of potential errors you want checked.
Andrew's made a start on some of these with the tools he develops in "The
Official Guide"
Sunanda.
[2/14] 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
<<quoted lines omitted: 8>>
> -- "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" ;
[3/14] from: sunandadh:aol at: 6-Jan-2002 17:37
Hi Joel,
Thanks for your thoughtful reply. I'll respond in one lump rather than trying
to pick out sentences to quote and comment on.
Implicit/explicit Return. This is a matter of style. I tend to always have an
explicit Return, to the extent of writing
return true
to show that I am NOT returning the result of the previous statement. I'd
expect a Lint dialect to enable me to turn that rule on, and you to turn it
off. That way we both get a usable Lint checker rather than none at all.
And yes, it would be difficult to write. And yes it may be derailed by
various dynamic changes as a program runs.
But who wants to write easy programs!? And I'd suspect most Rebol code would
not cause such derailings.
Rebol (at least on the web site) present itself as a language for
Internet/collaborative programming. But it seems in practice to make life
difficult for both those things:
-- I write some code that calls something from your site that calls something
from someone else's. And at some point it crashes with an "Error near a / b".
Is that my code or yours or the other person's or a Rebol-supplied mezzanine
function? I don't care how un-purist it is to expect the call stack to be
printed. But I need it at this point or the application is dead in the water.
-- I think we've all agreed that we find different styles harder to read than
others. So if you and I are collaboratively developing code, I'd want to
prettyprint yours into a style I prefer, and you'd do the same with mine.
This would open doors to collaborative programming.
I think there is a philosophical difference between your approach and mine.
You are making the case that it is difficult to do something 100%. Maybe that
makes you a purist. I'm saying, no worries there, give me the 75% that can be
done, and I'll live with the holes, or apply development standards to ensure
I don't go near them. I'm not sure what that makes me: a pragmatist or a
barbarian?
I hope you'll agree that we both have valid cases to make. I hope also that
RT are listening to this list and can apply their skills as language
designers to solve the problems I see while not damaging the purity we both
admire.
Sunanda.
[4/14] from: rotenca:telvia:it at: 7-Jan-2002 1:00
Hi Sunanda,
> Implicit/explicit Return. This is a matter of style. I tend to always have
an
> explicit Return, to the extent of writing
>
> return true
In Rebol it not always the same thing return or not return: attributes of
functions ([] [catch][throw]), for example, change the behaviour with or
without return.
Not only, look at these 2 examples:
>> a: func[][make error! "p"]
>> b: func[][return make error! "p"]
>> error? a
** User Error: p
** Near: make error! "p"
>> error? b
== true
>> a: func[][first [p:]]
>> b: func[][return first [p:]]
>> a
== p:
>> b
== p
I think Return must not be used only for style.
---
Ciao
Romano
[5/14] from: lmecir:mbox:vol:cz at: 7-Jan-2002 13:46
Hi Romano,
<<Romano>>
(...)
Not only, look at these 2 examples:
>> a: func[][make error! "p"]
>> b: func[][return make error! "p"]
>> error? a
** User Error: p
** Near: make error! "p"
>> error? b
== true
>> a: func[][first [p:]]
>> b: func[][return first [p:]]
>> a
== p:
>> b
== p
(...)
<</Romano>>
The first sample:
My POV is, that the first sample's function A should behave exactly like the
first sample's function B. (it is contained in my REP). If you see it like
me, send a request to feedback, please. If you don't, can you tell me your
preference?
The second sample:
Although I am not against a possibility to convert Rebol word types while
preserving their binding, I think, that this is a bug (discovered by you).
Could you please report it to feedback with a request to correct the
behaviour of the function A to yield the identical result as the function B
(and with an optional request to supply a binding preserving conversion) ?
Ciao
Ladislav
[6/14] from: joel:neely:fedex at: 7-Jan-2002 7:21
Hi, Ladislav and Romano
Ladislav Mecir wrote:
> Hi Romano,
> <<Romano>>
<<quoted lines omitted: 18>>
> The second sample:
> ... I think, that this is a bug (discovered by you).
Based on the documented (RCUG) meaning of RETURN, I agree
with Ladislav that both examples demonstrate bugs in REBOL.
As you know from the Expressions Chapter, blocks return
their last value when they return from evaluation:
do [1 + 3 5 + 7]
12
This is also true for functions. The last value is returned
as the value of the function:
sum: func [a b] [
print a
print b
a + b
]
print sum 123 321
123
321
444
In addition, the return function can be used to stop the
evaluation of a function at any point and return a value...
The verbiage and examples lead me to conclude that the only
difference between explicitly RETURNing a value and implicitly
returning the last value is that RETURN may be used to exit
prematurely
at any point within the function. Nowhere that
I can find does the documentation state (nor imply) that
f: func [...]
[ ...
some-expression
]
should be any different in value nor effect from
f: func [...]
[ ...
return some-expression
]
(particle physics notwithstanding... ;-)
-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" ;
[7/14] from: rotenca:telvia:it at: 8-Jan-2002 1:08
Hi Ladislav and Joel,
<Joel>
> Based on the documented (RCUG) meaning of RETURN, I agree
> with Ladislav that both examples demonstrate bugs in REBOL.
I think that the set-word return is a bug (already reported to Feedback), but
useful to extend to set-word binding preserving conversions.
I agree with Ladislav: we need a conversion function for any-words which be
binding-trasparent.
<Joel>
> The verbiage and examples lead me to conclude that the only
> difference between explicitly RETURNing a value and implicitly
> returning the last value is that RETURN may be used to exit
> "prematurely" at any point within the function. Nowhere that
> I can find does the documentation state (nor imply) that
About Return error!, i think it is a documented feature. The error exception
happens only when the error! is evaluated. To bypass or delay the evaluation,
you must pass it at a function: Return is a function, ergo...
The point here is that Return is a function, not an end-before-the-time of
block.
The difference is also evident when you think at [throw] which throw only
Return not every end of block.
I could agree with Ladislav when he asks a change in the actual error!
specific. But i do not think that the actual implementation of Return error!
is a bug.
<Ladislav>
>My POV is, that the first sample's function A should behave exactly like the
>first sample's function B. (it is contained in my REP). If you see it like
>me, send a request to feedback, please. If you don't, can you tell me your
>preference?
About Ladislav's Rep, i want to ask him:
0) The by-default-disabled error! is an object! or a error! datatype?
1) When an error is Fired, can it be catched by try?
2) Have you thought about throw err: try [...] ? Don't we need a sort of
fire-throw?
3) Normally when an error is throw, it is catchable without try. What happens
in your proposal?
I think that a big problem about the implementation of error and Return is its
interaction with functions attributes [catch] and [] (which throw a return
only if is argument is an error!). It is strange, undocumented or buggy (as
you like) and all these things constrain to make "strange" things with third
of functions to construct trasparent functions (like in Ladislav's
transp-func].
---
Ciao
Romano
[8/14] from: lmecir:mbox:vol:cz at: 8-Jan-2002 17:40
Hi Romano,
<<Romano>>
(...)
I could agree with Ladislav when he asks a change in the actual error!
specific. But i do not think that the actual implementation of Return error!
is a bug.
<</Romano>>
For me it doesn't matter whether it is a bug or not. I just prefer and
propose a more reasonable behaviour.
<<Romano>>
About Ladislav's Rep, i want to ask him:
0) The by-default-disabled error! is an object! or a error! datatype?
1) When an error is Fired, can it be catched by try?
2) Have you thought about throw err: try [...] ? Don't we need a sort of
fire-throw?
3) Normally when an error is throw, it is catchable without try. What
happens
in your proposal?
<</Romano>>
0) I prefer it to be an ERROR! datatype value.
1) I think, that this sample code can illustrate what I mean:
fire: func [[catch] error [error!]] [
throw error
]
error? try [fire make error! "my-error"]
fire make error! "my-error"
2) There are two possible answers to this question. The first one is: there
is no need to introduce a new function. Even now the [catch]/throw differs
from catch/throw (the former pair works only for errors and "fires" them,
the latter works for any Rebol value and doesn't necessarily fire errors).
The second one is, that because the mechanisms differ a lot, they should be
distinguishable. It is a matter of preference only.
3) I think, that the catch/throw mechanism (which differs from
[catch]/throw) should work as it does now.
<<Romano>>
I think that a big problem about the implementation of error and Return is
its
interaction with functions attributes [catch] and [] (which throw a return
only if is argument is an error!). It is strange, undocumented or buggy (as
you like) and all these things constrain to make "strange" things with third
of functions to construct trasparent functions (like in Ladislav's
transp-func].
<</Romano>>
There is a need to have transparent functions in Rebol. (They are useful for
new control functions creation.) My implementation is only a hack, because
it is a self-modifying code, which is ugly IMHO. The only reason, why it is
so ugly, is the behaviour of Rebol errors, which unreasonably complicates
things.
Cheers
Ladislav
[9/14] from: rotenca:telvia:it at: 8-Jan-2002 18:48
Hi Ladislav,
>> 2) Have you thought about throw err: try [...] ? Don't we need a sort of
>> fire-throw?
<<quoted lines omitted: 8>>
> 3) I think, that the catch/throw mechanism (which differs from
> [catch]/throw) should work as it does now.
And how to throw and fire an error? If we do in your proposal:
throw make error! "my-error"
we do not fire our error! and if there is no catch, we end with an
** Throw Error: No catch for throw: "my-error"
** Near: throw make error! "my-error"
if there is a catch we have not error at all.
instead of (not catched)
** Throw Error: ** User Error: my-error
** Near: throw make error! "my-error"
or (catched and evaluated)
** User Error: my-error
** Near: throw make error! "my-error"
we must always use the [catch] attribute to fire-throw the error?
And if the throw is a script file which some code do, instead of a sub func,
what happens?
> There is a need to have transparent functions in Rebol. (They are useful for
> new control functions creation.) My implementation is only a hack, because
> it is a self-modifying code, which is ugly IMHO. The only reason, why it is
> so ugly, is the behaviour of Rebol errors, which unreasonably complicates
> things.
I agree, but I think that the function attributes implementation requires a
deep revision.
---
Ciao
Romano
[10/14] from: lmecir:mbox:vol:cz at: 8-Jan-2002 19:26
Hi Romano,
<<Romano>>
(...)
And how to throw and fire an error? If we do in your proposal:
throw make error! "my-error"
we do not fire our error! and if there is no catch, we end with an
(...)
<</Romano>>
I thought, that I described it already. If we use the [catch]/throw
mechanism, we can fire an error. If we use catch/throw mechanism, we don't
fire an error. These two are different things.
<<Romano>>
we must always use the [catch] attribute to fire-throw the error?
<</Romano>>
I don't see any disadvantage of that, use the FIRE function I defined, if
you prefer.
<<Romano>>
I agree, but I think that the function attributes implementation requires a
deep revision.
<</Romano>>
Aha, that is where you are coming from. I do not think that the revision is
needed.
Ciao
Ladislav
[11/14] from: greggirwin:mindspring at: 8-Jan-2002 11:48
Hi Sunanda, et al
I didn't jump in on the style thread, but I can include my thoughts here as
they are applicable in this context as well.
<< -- I write some code that calls something from your site that calls
something
from someone else's. And at some point it crashes with an "Error near a /
b".
Is that my code or yours or the other person's or a Rebol-supplied mezzanine
function? I don't care how un-purist it is to expect the call stack to be
printed. But I need it at this point or the application is dead in the
water. >>
I think this points out that we, as REBOL developers, need to take into
account the context in which our code is intended to be used. I can
appreciate being able to write simple reblets that are self-contained and
often times may get by with a minimum of error handling and formal design.
If you are writing code to be used in distributed applications, you darn
well better have everything error trapped and be as helpful as possible when
it comes to tracking down errors. I think Design by Contract will be
enormously helpful in that regard but I haven't had time to work up a
dialect implementing it yet.
That said, I think call stack information could be very useful in certain
cases as well.
<< -- I think we've all agreed that we find different styles harder to read
than
others. So if you and I are collaboratively developing code, I'd want to
prettyprint yours into a style I prefer, and you'd do the same with mine.
This would open doors to collaborative programming. >>
I'm not sure about this. My thoughts about style are based on only 6 months
of experience with REBOL, and I have yet to find my REBOL "voice". What I
*am* finding, is that I use different styles in different contexts. The
style I use when writing in the Layout dialect is different than what I use
when writing in my FSM dialect. For pure REBOL code, I have some code that
is very much block style (style B) but I have other code which is very
concise.
I think that my goal will be to write code is that easily understood, and
easily maintained and I don't know that a single "style" will be the best
fit in all cases for REBOL code. For example, if you are writing a novel,
you would use a different style than if you were writing an instruction
manual or a "How To" booklet. Writing an article for a technical journal
would be different still. You may also have a large amount of data mixed in
with your code (think Easy-VID, or embedded instructions in an app), what do
you do with the data, style wise?
How easy something is to maintain doesn't necessarily mean that updates
requiring the minimum number of keystrokes are the best. I believe in making
the behavior and *intent* as clear and obvious as possible, which doesn't
necessarily lock you in to a single style with REBOL nearly as much as with
other languages, IMO.
If we go down the pretty-printer route, it will dissect a carefully
formatted program modeled on the idea of "Literate Programming" and possibly
destroy all the structure and meaning contained therein. Yes, that's an
extreme example, but I think REBOL will really change the way *I* write
programs (it already has) and eventually we'll discover more appropriate
tools, which aren't necessarily based on tools we've used for other
languages historically.
A pretty-printer could still be useful for a certain body of code, to be
sure, but there are wide variety of styles in the library and I don't think
the style someone used has anything to do with how easily I can decipher the
code. The overall organization of a script affects that to a much greater
extent. I.e. the more I have to keep in my head, which isn't visible on the
screen I'm looking at, the more difficult it is to decipher.
Just some thoughts from a baby REBOL. :)
--Gregg
[12/14] from: lmecir:mbox:vol:cz at: 8-Jan-2002 19:50
Hi,
<<Romano>>
(...)
2) Have you thought about throw err: try [...] ? Don't we need a sort of
fire-throw?
(...)
<</Romano>>
I thought about it once again. It looks really as a better idea to
distinguish between [catch]/throw and catch/throw. The former should really
look different, e.g. like [e-catch]/e-throw as opposed to catch/throw. That
could eliminate some nasty interferences, which is A Good Thing.
Ciao
Ladislav
[13/14] from: joel:neely:fedex at: 8-Jan-2002 13:32
Hi, Greg, Sunanda, et al...
Gregg Irwin wrote:
> > -- I write some code that calls something from your site
> > that calls something from someone else's. And at some point
<<quoted lines omitted: 15>>
> regard but I haven't had time to work up a dialect implementing
> it yet.
Let's not forget what REBOL alreay provides, which most of us
(myself included) probably don't use to full benefit. Pretend
that I have some REBOL source files from elsewhere:
8<----"buggyfunc.r"---------------------------------------------
REBOL []
buggyfunc: func [a [integer!] b [integer!]] [a / b]
8<--------------------------------------------------------------
and
8<----"buggywrap.r"---------------------------------------------
REBOL []
buggywrap: func [n [integer!] /local r]
[ r: copy []
either even? n
[ repeat i n [append r 5040 / i]]
[ repeat i n [append r 5040 / (8 - i)]]
r
]
8<--------------------------------------------------------------
and now I want to use those bits of scriptage myself.
I can write:
8<----"buggytest.r"---------------------------------------------
REBOL []
do %buggyfunc.r
do %buggywrap.r
testbug: func [n [integer!] /local r]
[ either error? r: try [buggywrap n]
[ print ["Oops! buggywrap blew up when called with" n "!"]]
[ print ["Pretend to do something useful" newline tab mold r]]
]
8<--------------------------------------------------------------
which wraps the "untrusted" code in a TRY evaluation to let me
know whether (and under what circumstances) the foreign code may
cough and die.
>> do %buggytest.r
>> testbug 5
Pretend to do something useful
[720 840 1008 1260 1680]
>> testbug 6
Pretend to do something useful
[5040 2520 1680 1260 1008 840]
>> testbug 7
Pretend to do something useful
[720 840 1008 1260 1680 2520 5040]
>> testbug 8
Pretend to do something useful
[5040 2520 1680 1260 1008 840 720 630]
>> testbug 9
Oops! buggywrap blew up when called with 9 !
I suspect that some creative uses of TRY would go a long way toward
addressing the issues of making more robust code (even when it uses
other, untrusted, foreign components).
-jn-
[14/14] from: greggirwin:mindspring at: 8-Jan-2002 13:04
hi Joel,
<< I suspect that some creative uses of TRY would go a long way toward
addressing the issues of making more robust code (even when it uses
other, untrusted, foreign components). >>
Absolutely! In addition to TRY, a nice implementation of assertions (i.e.
Design by Contract), and tools which make use of REBOL's reflective
properites, will certainly help us build larger, more reliable, systems.
Undoubtedly, just as with other languages, the tools will grow up around us
as the need arises.
--Gregg
Notes
- Quoted lines have been omitted from some messages.
View the message alone to see the lines that have been omitted