Try this in your favorite repl:
Define a function, foo, that calls some other function, bar, that is not yet defined. Now call foo. What happens?
Obviously, the call to foo breaks, because bar is not defined. But what happens when it breaks? What happens next?
If your favorite repl is Python’s or Ruby’s or any of a few dozen other modern repls, the answer is most likely that it prints an error message and returns to its prompt. In some cases, perhaps it crashes.
So what’s my point, right? What else could it do?
The answer to that question is the “differentiating point” of repl-driven programming. In an old-fashioned Lisp or Smalltalk environment, the break in foo drops you into a breakloop.
A breakloop is a full-featured repl, complete with all of the tools of the main repl, but it exists inside the dynamic environment of the broken function. From the breakloop you can roam up and down the suspended call stack, examining all variables that are lexically visible from each stack frame. In fact, you can inspect all live data in the running program.
What’s more, you can edit all live data in the program. If you think that a break was caused by a wrong value in some particular variable or field, you can interactively change it and resume the suspended function. If it now works correctly, then congratulations; you found the problem!
Moreover, because the entire language and development system are available, unrestricted, in the repl, you can define the missing function bar, resume foo, and get a sensible result.
Mulle maksetaan clojuren tunkkaamisesta. Palaudun siitä tunkkaamalla Common Lispiä. Mitä enemmän tunkkaan CL:ää, sitä enemmän ahdistaa clojurea tunkatessa JVM:n poikkeukset ja se, kuinka käytettävyysongelmainen räpellys ciderin debugger on. Stackframen lexical environmenttiin saa nykyään REPLin, jolla pystyy räpeltämään ympäristöä niin paljon kuin se clojuressa käy järkeen, mutta pelkkä funktioiden uudelleenkirjoittelu saa REPLin... tilohin.
Restarteista mussuttaminen lienee ihan turhaa, koska prosessin ollessa jumissa debuggerissa ei ympäristöä voi muutenkaan muokata :D
Prosessin ulkopuolista ympäristöä voi muokata mielin määrin vaikka oltaisiinkin jumissa debuggerissa. Yrität käynnistää common lisp -prosessia, joka käynnistyessään ottaa yhteyttä kantaan, mutta oot unohtanut käynnistää kannan? Ei se mitään, käy kääntymässä *shell*issä (aja `docker-compose start`), palaa slimen virhepuskuriin ja paina "Retry connecting" restarttia. Prosessin käynnistyminen jatkuu.
Sama clojuressa? Kantayhteyden epäonnistuminen aiheuttaa (varmaan vielä javan puolella...) `throw new jdbcEsimerkkiVirheException()`in. Kuka mitään pinosta löytyvää kontekstia virheelle tarvitsisi, parasta heittää kaikki roskikseen. Tarpeeksi huonosti käsin rakennetussa softalaturissa tää jättää myös replin tilan hyvin määrittelemättömäksi. `(go)` kutsu saattaa aloittaa lataamisen alusta, se saattaa tarjota uusia, outoja virheitä, kuka tietää?
... nyt kun oikein ajattelen, niin sovelluksen käynnistyminen ois aika ideaali paikka käyttää STMää.
Toki clojuressa on hyvätkin puolensa, kuten lisp-1, standardikirjasto joka ei oo ihan seinähullu, kivoja rakenteita kielessä jotka on toteutuskelvottomia Lispissä (destrukturointi-kieli on taideteos <3), se että kukaan maksaa suomessa clojuren tunkkaamisesta... mutta tää poikkeus/debugger-tarina vakuuttaa mua päivä päivältä vähemmän :D