
REPL Debugging: No Stacktrace Required (2017) - simonpure
http://blog.cognitect.com/blog/2017/6/5/repl-debugging-no-stacktrace-required
======
jedberg
Fun REPL story: Reddit was originally written in LISP, so you could also
manipulate the live running code with the REPL.

Sometimes Steve would do that to add a new feature or change some broken code.

The danger was that if you didn’t copy/paste the fix back to the saved source,
the problem would return when the app restarted!

~~~
eadmund
It'd be pretty simple — albeit so very much a bad idea — to run the REPL from
a stream which saved everything typed in the REPL to a log read in at startup.
A fancier version might allow log editing. A _really_ fancy version might save
everything to a log and offer commands to view expressions in the log & copy
the desired ones to the startup file, or another file.

Or one could just remember to move corrected source back to the right place.

This is probably somewhere where a Smalltalk image-based browser would be
preferable — it would/could unify the REPL and editing source.

------
lispm
> At this point it is pretty clear that calling first on a number is not
> valid. A quick doc check will show that first is for collections

I could just have the compiler explain the problem in the REPL, for example
with SBCL:

    
    
      * (defun foo (n)
          (cond ((> n 40) (+ n 20))
                ((> n 20) (- (first n) 20))
                (t 0)))
      ; in: DEFUN FOO
      ;     (FIRST N)
      ; ==>
      ;   (CAR N)
      ; 
      ; caught WARNING:
      ;   Derived type of N is
      ;     (VALUES
      ;      (OR (DOUBLE-FLOAT (20.0d0) 40.0d0) (SINGLE-FLOAT (20.0) 40.0)
      ;          (RATIONAL (20) 40))
      ;      &OPTIONAL),
      ;   
      ;   conflicting with its asserted type
      ;     LIST.
    

Then there is less need for manually searching for problematic expressions in
the code.

See [http://sbcl.org](http://sbcl.org)

~~~
bmn__
Did you miss the premise near the beginning of the article?

> I am going to throw away all the potentially useful information

~~~
lispm
From the article:

> read stacktraces and add instrumentation for tracing. In this blog post I am
> going to debug his example program without using such tooling, relying only
> on the rapid feedback loop provided by the REPL

One does not need error reporting, stacktraces or add instrumentation, while
running the code. SBCL gives the clue without any of that, before (!) the code
runs. In the REPL. The incremental compiler makes some of that manually
searching for type problems in the code unnecessary, while supporting REPL
use.

------
dimitar
I was talking with a Data Scientist friend abiut what I love so much about
Clojure and it was largely about the REPL. To him it sounded like nothing
special - trying expressions in a prompt is hardly new or original and he does
all the time in R. Which I thought is actually a really good selling point for
Clojure - you get to do the really high-level data manipulation you do in R,
Mathematica or even Python in a language that is compiled, good with
concurrency, probably embraces your platform of choice and is also used for
the frontend.

What makes it different for me is two things: \- the functional style mean
that many things are much easier to track in a REPL - you don't need to use a
stepping debugger to get to the troublesome value of i as often.

\- Structural editing using parinfer or similar is fun.

Overall I'm not convinced that the Clojure REPL is that unique, but combined
with the rest of the ecosystem it is pretty sweet.

~~~
ss3000
I think the major difference between the Clojure REPL experience and most
other language's REPLs that I've worked with is the fact that Clojure REPLs
can connect to a running stateful system (or a situated program as Rich Hickey
coined it), and allows you to explore and manipulate the system's state and
behavior dynamically at runtime, which makes it an incredibly powerful tool
for building and debugging such a system.

Most REPLs I've used in other languages are strictly for evaluating individual
commands in an isolated, stateless manner.

~~~
yogthos
I think immutability plays a big role here as well. Clojure makes it natural
to structure your code in a way where things can be reloaded individually
without having to worry about global state. The REPL would be of much more
limited use in a language where everything is coupled via shared mutable
references.

This can also be seen with ClojureScript where hot loading works reliably out
of the box, while all attempts at do hot loading in Js are flaky to the point
of not being of much use.

~~~
lispm
> The REPL would be of much more limited use in a language where everything is
> coupled via shared mutable references.

That would be surprising, since the REPL was developed for Lisp in the 60s,
which actually works that way.

~~~
pixelperfect
That isn't a contradiction - the advantages of a REPL are greater for Clojure
than for Lisp.

~~~
lispm
From what I see that seems not to be the case. Reloading code in REPLs in Lisp
is widely used.

------
shivekkhurana
REPL is a killer Clojure feature that nobody (outside the ecosystem) talks
about.

Probably it's hard to comprehend what developing in a REPL feels like, majorly
because no other commercial language has a REPL as powerful as Clojure.

I gave a talk[1] explaining the REPL and most people in the audience
(including senior Java, C# and Python developers) had never seen something
like that before.

REPL is the reason why I (and perhaps other Clojure devs) endure the pain
around Clojure tooling, demand, supply and ecosystem in general.

[1]
[https://www.youtube.com/watch?v=Bs44qdAX5yo](https://www.youtube.com/watch?v=Bs44qdAX5yo)

~~~
Scarbutt
A good REPL is nice, but its hardly a killer feature or a big productivity
boost. If Clojure didn't have a REPL it would have been completely dead by now
cause its startup time is really bad. Clojure needs to have a REPL in order to
survive (which is barely doing).

Programmers are equally productive[0] (or way more because of their big
ecosystems) in languages that have a fast code-run cycle.

[0]
[https://groups.google.com/forum/#!topic/comp.lang.lisp/L5dZ-...](https://groups.google.com/forum/#!topic/comp.lang.lisp/L5dZ-j6Id-s%5B76-100%5D)

~~~
yogthos
REPL driven workflow allows me to put the application in a specific state, and
then continue developing features against that state. If you have to restart
the application each time you make a code change then you have to rebuild that
state each time. This creates a much slower feedback loop, especially when
you're working on large applications. Furthermore, the REPL is a fantastic
tool for troubleshooting live systems in production, and in some cases even
patching them with zero downtime.

I don't know a single person who's worked with this workflow on a serious
project and would want to go back to compile/run cycle afterwards.

Meanwhile, Clojure leverages host ecosystem getting all the same benefits as
any other language running on it.

------
slifin
When talking about the Clojure REPL I think a lot of people forget that the
language matters a lot

Clojure focuses on being dynamic, to give an example if in PHP you wanted to
attach an interface to a class without restarting the REPL I don't think you
could

In Clojure that's supported via a function call

------
fmakunbound
> Broken! HaHa!

I think this is about equivalent to the error messages one already gets from a
typical Clojure REPL session, so no loss of potentially useful information,
after all.

------
elcomet
how do you explore the running code in a repl, if it was modified while
running ?

