Thank you for your thought, Dan. Here are some of mine in return.

I would say that there are several separate things going on here and we'd
benefit from separating them in our discussion. As I understand them,
contract violations are separate from stack traces. A contract violation is
the discovery that something gone wrong in a specific program state (which
is what essentially all of the fields in the OP's message show). A stack
trace tries to reconstruct something about the context of that call in the
underlying VM and is subject to the vagaries of things happening in
Racket's optimizer. You can get stacktraces at any point in a Racket
program [1] and they are useful for other kinds of debugging than just from
figuring out when an error occurred.

Anyway, here's my attempt to reconstruct from the error message, a program
that behaves like the OP, but run with our better stack printing mechanism
(errortrace, it's called) turned on.

>From my terminal window:

$ cat abc.rkt
#lang racket
(provide
 (contract-out
  [add-number (-> number? number? number?)]))
(define (add-number x y) (+ x y))
$ cat edf.rkt
#lang racket
(require "abc.rkt")
(add-number "1" 2)
$ racket -l errortrace -t edf.rkt
add-number: contract violation
  expected: number?
  in: the 1st argument of
      (-> number? number? number?)
  contract from: /Users/robby/Desktop/abc.rkt
  blaming: /Users/robby/Desktop/edf.rkt
  at: /Users/robby/Desktop/abc.rkt:4.3
  errortrace...:
   /Users/robby/Desktop/edf.rkt:3:0: (lifted.0 (quote "1") (quote 2))
   /Users/robby/Desktop/edf.rkt:3:0: (call-with-values (lambda () (lifted.0
(quote "1") (quote 2))) print-values)
  context...:

 /Users/robby/git/plt/racket/collects/racket/contract/private/blame.rkt:135:0:
raise-blame-error
   /Users/robby/Desktop/edf.rkt: [running body]

As you can see, the source location of the call is there (those "lifted.0"s
are unfortunate, however).

There are no values of local variables, however. Last time I checked,
errortrace runs at about a factor of 3 slowdown to just record the precise
stack traces that it does record (and as the compiler gets better, the gap
gets bigger) so the right tradeoff isn't very clear there either. And,
given Racket's "language workbench" capabilities, this is something that
anyone can experiment with. errortrace is a library that is implemented by
traversing a fully expanded program and dropping in with-continuation-marks
in just the right places. Then these marks can be read back and
reconstructed into a stack trace. The debugger works the same way, except
that it's instrumentation actually does capture local variables. I don't
know the relative performance of the two, but we could certainly experiment
with using something more like the debugger's annotations.

Returning to the original message in the thread, I don't know how the OP
wrote their program in a way to get no stacktrace information at all;
possibly it was at the top-level, in which case there was no stacktrace to
report and I guess in 'real life' one wouldn't complain in this case anyway.

I have given a little bit of thought to enhancing the arrow contract
combinators (things like -> and ->* and ->i) so the detect the case when a
first-order portion of one of the argument contract fails and then report
all the other arguments, which would, in this case, report the number 2 in
the edf.rkt file, something missing from the error messages that'd be nice
to see. I'm not sure how to do without hurting performance in an
unacceptable way. But probably I just haven't tried hard enough.

Robby

[1]  I find myself writing this little program fragment a lot when I can't
figure how code is getting to some bad spot:

(printf "----------\n")
(for ([x (in-list
          (continuation-mark-set->context
           (current-continuation-marks)))])
  (printf "  ~s\n" x))
(printf "----------\n")

It can be very helpful.



On Sun, Sep 8, 2013 at 4:06 PM, Daniel Prager <daniel.a.pra...@gmail.com>wrote:

> For the consideration of Robby et al:
>
> The clearest run-time errors I ever received were dumped out of
> SmallEiffel / SmartEiffel in response to contract violations.
>
> These were stack-traces that -- critically -- included parameter values
> and local state information for each stack frame.  Blame for pre- and
> post-condition violations were easy to read off, down to the line / column.
>
> Example from http://www.minimalprogramming.org/proglang.pdf pp177-:
>
> *** Error at Run Time ***: Require Assertion Violated.
> *** Error at Run Time ***: consistent_balance
> 3 frames in current stack.
> ===== Bottom of run-time stack =====
> System root.
> Current = TEST#0x8061a60
> line 4 column 2 file ./test.e
> ======================================
> make TEST
> Current = TEST#0x8061a60
> account = Void
> line 8 column 4 file ./test.e
> ======================================
> make ACCOUNT
> Current = ACCOUNT#0x8061a88
>
>   [ minimal_balance = 0.000000
>
>     balance = 0.000000
>
>   ]
>
> a_minimal_balance = 100.000000
> initial_balance = 10.000000
> line 7 column 42 file ./account.e
> ===== Top of run-time stack =====
> *** Error at Run Time ***: Require Assertion Violated.
> *** Error at Run Time ***: consistent_balance
>
>
> The author remarks, "That’s what I call a comprehensive error description.
> Not only do we get the name of the violated condition and the values of the
> parameters passed to the constructor method, but also the state of the
> account object in question."
>
> This could be improved further.  When I rolled my own contracts for C#
> (pre-MS code contracts) I captured and reprocessed the stack trace to
> included a "----->" to point at the line/column reference to the calling
> code (for a pre-condition violation) or the supplying code (for a
> post-condition violation).  I also reversed the direction of the trace so
> that errors would appear at the top, thereby avoiding having to read down a
> long trace when the error was usually apparent from the the top.
>
> But those aspects are sugar, although pedagogically usefully and
> practically very convenient.  Frustratingly, what I couldn't do, was dump
> out parameters (and state) automatically, necessitating manual annotation
> in the contract violation messages (which then became a risk/reward
> trade-off).
>
> Cheers
>
> Dan
>
>
> On Sun, Sep 8, 2013 at 11:40 PM, Robby Findler <
> ro...@eecs.northwestern.edu> wrote:
>
>> You are not the only one to complain :). We've been having an internal
>> discussion about this, actually.
>>
>> In this particular, case, I think that you are using provide/contract,
>> right? If so, the blame really lies with the entire file edf.rkt. So there
>> really isn't anything more specific to say.
>>
>> Robby
>>
>>
>>
>> On Sun, Sep 8, 2013 at 4:20 AM, antoine <antoine...@gmail.com> wrote:
>>
>>> Hello,
>>>
>>> I have just start using contracts in racket, but i am a bit disapointed
>>> by the
>>> error message they give to me.
>>>
>>> example contract error:
>>>
>>>  add-number: contract violation
>>>  expected: number?
>>>  given: "1"
>>>  in: the 2nd argument of
>>>       (-> number? number? number?)
>>>  contract from: abc.rkt
>>>  blaming: edf.rkt
>>>  at: abc.rkt:5.3
>>>
>>> I would have expected to have the line and column number in the blaming
>>> line
>>> like what you could get from a stacktrace.
>>>
>>> Is it possible in contracts?
>>> Am i the only one to complain? :)
>>>
>>> Thanks you in advance.
>>> ____________________
>>>  Racket Users list:
>>>  http://lists.racket-lang.org/**users<http://lists.racket-lang.org/users>
>>>
>>
>>
>> ____________________
>>   Racket Users list:
>>   http://lists.racket-lang.org/users
>>
>>
>
>
> --
> *Daniel Prager*
> Agile/Lean Coaching, Software Development and Leadership
> Twitter: @agilejitsu <https://twitter.com/agilejitsu>
> Blog: agile-jitsu.blogspot.com
>
____________________
  Racket Users list:
  http://lists.racket-lang.org/users

Reply via email to