[REBOL] Re: Beginner's questions on REBOL/Core and REBOL/View
From: joel:neely:fedex at: 11-Sep-2002 22:43
Hi, Sunanda, and all,
The problem with small examples is that they're... small. This
sometimes makes things look trivial that really aren't, IMHO.
However, I'm interested in your opinions of the constructive
suggestions near the end...
[SunandaDH--aol--com] wrote:
> ... Rebol gives far too little away when there is an error --
> maybe Carl S doesn't make stupid coding errors like the rest
> of us.
>
> Let me modify your code to have a bug in it ... And run it.
> We get:
>
> ** Math Error: Attempt to divide by zero
> ** Near: print b: 2 / 0
>
...
> Better context information *is* in there somewhere -- try this:
> the same error in a VID action facet:
>
> unview/all
> view layout [
> button "click me"
> [
...
> ]
> ]
>
> This fails in an easier to find way -- better context
> information:
>
> ** Math Error: Attempt to divide by zero
> ** Where: func [face value][
> a: [print b: 2 / 0]
> c: [2 / 0]
> d: append a c
> insert d first [print]
> do d
> ]
> ** Near: print b: 2 / 0
>
Note that this shows quite a bit of stuff that appears nowhere
in our source code: the
func [face value][
bit, as well as the block to which A is being set. This would
also be likely to confuse the typical beginner IMHO.
However...
Let's try that in Core (or at least the moral equivalent...)
>> crash: func [/local a c d] [
[ a: copy [b:]
[ c: [2 / 0]
[ d: append a c
[ insert d first [print]
[ do d
[ ]
>> crash
** Math Error: Attempt to divide by zero
** Where: crash
** Near: print b: 2 / 0
Notice that middle line
** Where: crash
which actually tells us where to look for the expression which
caused the math error. If we define and use reasonably small
functions, this narrows our scope quite a bit. Going further,
we can actually reproduce your more verbose case, as follows:
>> do func [/local a c d] [
[ a: copy [b:]
[ c: [2 / 0]
[ d: append a c
[ insert d first [print]
[ do d
[ ]
** Math Error: Attempt to divide by zero
** Where: func [/local a c d][
a: copy [b:]
c: [2 / 0]
d: append a c
insert d first [print]
do d
]
It appears that REBOL is trying (in all cases???) to show us the
exact structure/expression being evaluated when the math error
occurred. It's just that this second case involves a larger
expression, perhaps.
While I might agree that more detailed information would be
helpful, I still maintain that it doesn't relate to "source line"
in many cases. I haven't had time to play with the preprocessor,
but I suspect the concept of "source file" becomes even more
obscured when we #INCLUDE source from multiple places (especially
when nested...)
If we change the example just a bit more, we can get a case for
which source file concepts just don't apply at all.
crash-setup: func [i [integer!] j [integer!]] [
append copy [print] reduce [i to-word "/" j]
]
random-range: func [dn [integer!] up [integer!]] [
(random up - dn + 1) + dn - 1
]
crash-prep: func [
n [integer!] lo [integer!] hi [integer!]
/local result
][
result: make block! n
loop n [
append/only result
crash-setup
random-range lo hi
random-range lo hi
]
]
crash-wrapper: func [
n [integer!] bottom [integer!] top [integer!]
][
foreach item crash-prep n bottom top [
do item
]
]
crash-wrapper 20 0 5
Evaluating the above yields a nice puzzle:
...
>> crash-wrapper 20 0 5
1
4
0
0
3
1.5
0.333333333333333
1.66666666666667
5
1.5
0.5
2
2.5
1.25
0.333333333333333
** Math Error: Attempt to divide by zero
** Where: crash-wrapper
** Near: print 4 / 0
My first question is this: What more could REBOL tell us at
this point? We're told that the problem occurred during the
evaluation of CRASH-WRAPPER, but the body of that function
has little to do with the actual expression being evaluated
at the point of error.
My second question is: How much responsibility do I have as
the programmer for detecting and managing errors that may arise
during evaluation?
REBOL is a very powerful language, but with increased power must
come increased responsibility. For example, I could have done
some more work to handle errors myself:
crash-wrapper2: func [
n [integer!] bottom [integer!] top [integer!]
/local risky-blocks
][
foreach item risky-blocks: crash-prep n bottom top [
if error? try [
do item
][
print [
"Error DOing" mold item newline
"within" mold risky-blocks
]
halt
]
]
]
crash-wrapper2 20 0 5
which certainly gives me more context to think about:
>> crash-wrapper2 20 0 5
3
4
5
0
Error DOing [print 4 / 0]
within [[print 3 / 1] [print 4 / 1] [print 5 / 1] [print 0 / 1]
[print 4 / 0] [print 2 / 3] [print 1 / 0] [print 3 / 4]
[print 5 / 3] [print 2 / 2] [print 3 / 3] [print 1 / 2]
[print 4 / 2] [print 3 / 2] [print 4 / 5] [print 0 / 4]
[print 0 / 1] [print 4 / 3] [print 4 / 5] [print 1 / 5]]
Now let me suggest a couple of possibilities:
1) Could we (the list community) describe any additional context
information which RT might be able to add to error messages?
For example: I suspect that REBOL internally has some sort
of representation for all expressions pending evaluation (the
moral equivalent of a "stack", for example). Would it be
useful/feasible for an uncaught error to dump some portion
of that information to the console? Having a more thorough
view of what was going on might help the programmer locate
and diagnose an error, but we might want some way to control
the amount of information displayed (have you ever seen a Java
stack trace, for example... ;-) such as limiting the number of
levels to be displayed by setting a system variable.
2) Could we (the list community) define "safe" equivalents to some
of the more common error-prone operations (e.g. DO, FUNC, ...)
so that one could write and test code with the training wheels
on?
For example: A paranoid SAFE-DO accepting a block or function
as its argument could check for errors during evaluation and
dump the block or function to the console if something went
wrong. A SAFE-FUNC could wrap its argument in a try block to
dump the arguments to the console in case of an error.
I could even imagine a SET-SAFE and SET-UNSAFE word pair that
would redefine DO and FUNC and etc. to be the checked or un-
checked versions, so that the same source could admit both
checked (but slower) and unchecked (and faster) evaluations.
-jn-
--
; Joel Neely joeldotneelyatfedexdotcom
REBOL [] do [ do func [s] [ foreach [a b] s [prin b] ] sort/skip
do function [s] [t] [ t: "" foreach [a b] s [repend t [b a]] t ] {
| e s m!zauafBpcvekexEohthjJakwLrngohOqrlryRnsctdtiub} 2 ]