Hacker News new | ask | show | jobs
by lisper 1992 days ago
It can be incredibly handy if you are doing a computation with a lot of state. I'm currently working on a tool that does chip design, the state for which runs into hundreds of gigabytes. Being able to redefine a class without restarting can be an absolute life saver in a situation like that.
2 comments

Exactly. For exploratory programming with long running computations losing all your state because of something you could fix by hot-reloading a one line change can be rather frustrating -- I remember cursing at python years ago when I was running some ML experiments and more than once lost all data right at the end due to some silly bug at the serialization stage that would have been one line to fix.

Mind you, with python you can at least run things with ipython --pdb to get thrown into a debugger on failure so you can potentially serialize some state before losing it. And stacktraces are better than common lisp's. But having a robust way to redefine stuff or fixing up a failed computation is definitely very handy in some contexts.

> stacktraces are better than common lisp's

Stack traces are not standardized in Common Lisp, so this is a non-sensical statement. At worst you could say that Python's stack traces are better than some particular CL implementation, but not CL in general.

And almost certainly, if you don't like the way your CL presents stack traces, you can easily change it.

> Stack traces are not standardized in Common Lisp, so this is a non-sensical statement.

Yes, someone could have secretly implemented a common lisp implementation that has the most ergonomic stacktraces in the whole wide world or, equally, a posix shell that runs numerical code a gazillion times faster than the equivalent C compiled with gcc -O3 -mnative -ffast-math because after all the relevant standards do not explicitly forbid it!

I'm not quite sure why lispers in particular are so in love with this argument.

There is nothing nonsensical in saying that shell is a truly terrible language to write high performance numerical code in, even if that is not true by some sort of logical necessity.

In practice most languages have a dominant implementation (even those with ISO standards) and a range of capabilities that existing (and likely future) implementations fall within. Both are far more important than what standards say (try compiling code with djb's standard conformant usage of errno some time).

Can you give an example of Python stacktrace being better than CLs? It hasn't been true in my experience, so I'm wondering.
A good example would take a fair amount of space, but let's try this bogus example:

This is ipython:

    In [2]: os.path.join(None)
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-2-ba05fdaae739> in <module>
    ----> 1 os.path.join(None)

    /opt/anaconda3/lib/python3.8/posixpath.py in join(a, *p)
         74     will be discarded.  An empty last part will result in a path that
         75     ends with a separator."""
    ---> 76     a = os.fspath(a)
         77     sep = _get_sep(a)
         78     path = a

    TypeError: expected str, bytes or os.PathLike object, not NoneType
This is SBCL:

    * (merge-pathnames nil)

    debugger invoked on a TYPE-ERROR in thread
    #<THREAD "main thread" RUNNING {1000560083}>:
      The value
        NIL
      is not of type
        (OR (VECTOR CHARACTER) (VECTOR NIL) BASE-STRING PATHNAME SYNONYM-STREAM
            FILE-STREAM)

      when binding PATHNAME

    Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

    restarts (invokable by number or by possibly-abbreviated name):
      0: [ABORT] Exit debugger, returning to top level.

    (MERGE-PATHNAMES NIL 70256781343884 MERGE-PATHNAMES) [external]
    0]
I find the first much quicker to read and parse (better layout, no SHOUTING, color coded, context info) and you can immediately see what file and location you'd need to "fix". What is an example were you prefer the lisp stacktrace to something you'd get in interactive development with ipython or in production with newrelic or anything else that captures python stacktraces?
The difference is that SBCL caught the error directly on entry of MERGE-PATHNAMES. SBCL did on call to MERGE-PATHNAME a runtime type check. It knows the expected types for the arguments.

SBCL told you that the call to MERGE-PATHNAME is already wrong. A backtrace then will only show higher up code from the environment and the call to MERGE-PATHNAME.

Your Python code went into the routine...

Often the Python backtrace will be easier to understand, since it is source/line oriented, since Python code usually does not have extensive code transformations (-> Lisp macros) and using an optimizing compiler like SBCL may make the code less debuggable (for example when using tail call optimization).

You didn't post CL's stacktrace.
I did. There just isn't much of a callstack, because I call a single function with an invalid argument.

    * (merge-pathnames nil)

    debugger invoked on a TYPE-ERROR in thread
    #<THREAD "main thread" RUNNING {10010B0523}>:
    The value
        NIL
    is not of type
        (OR (VECTOR CHARACTER) (VECTOR NIL) BASE-STRING PATHNAME SYNONYM-STREAM
            FILE-STREAM)

    when binding PATHNAME

    Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

    restarts (invokable by number or by possibly-abbreviated name):
    0: [ABORT] Exit debugger, returning to top level.

    (MERGE-PATHNAMES NIL 4946604 MERGE-PATHNAMES) [external]
    0] :backtrace

    Backtrace for: #<SB-THREAD:THREAD "main thread" RUNNING {10010B0523}>
    0: (MERGE-PATHNAMES NIL 4946604 MERGE-PATHNAMES) [external]
    1: (SB-INT:SIMPLE-EVAL-IN-LEXENV (MERGE-PATHNAMES NIL) #<NULL-LEXENV>)
    2: (EVAL (MERGE-PATHNAMES NIL))
    3: (INTERACTIVE-EVAL (MERGE-PATHNAMES NIL) :EVAL NIL)
    4: (SB-IMPL::REPL-FUN NIL)
    5: ((FLET "LAMBDA0" :IN "SYS:SRC;CODE;TOPLEVEL.LISP"))
    6: (SB-IMPL::%WITH-REBOUND-IO-SYNTAX #<CLOSURE (FLET "LAMBDA0" :IN "SYS:SRC;CODE;TOPLEVEL.LISP") {96F7CB}>)
    7: (SB-IMPL::TOPLEVEL-REPL NIL)
    8: (SB-IMPL::TOPLEVEL-INIT)
    9: ((FLET SB-UNIX::BODY :IN SAVE-LISP-AND-DIE))
    10: ((FLET "WITHOUT-INTERRUPTS-BODY-7" :IN SAVE-LISP-AND-DIE))
    11: ((LABELS SB-IMPL::RESTART-LISP :IN SAVE-LISP-AND-DIE))
    12: ("foreign function: #x43270B")
    13: ("foreign function: #x403F08")

    0]
can you share what you're working on? is this open?