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...

[EMAIL PROTECTED] 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 ]
-- 
To unsubscribe from this list, please send an email to
[EMAIL PROTECTED] with "unsubscribe" in the 
subject, without the quotes.

Reply via email to