Live data from Hacker News

Why your REPL experience sucks

srasu.srht.site

21–30 of 58 posts

Re: Why your REPL experience sucks

#21

Earlier quoted context omitted.

This feels a little too strong to me. DrRacket has as its fundamental UI flow a combination of whole-reloaded-file and a REPL, and obviously the creators of Racket are intimately familiar with Lisp REPLs. One problem being addressed is the discrepancy between REPL-based development and non-image-based languages: you have to get your code back out into a source file somehow, without forgetting anything. In image-based…

> In image-based languages like Smalltalk and APL, you just save your environment after modifying it interactively; there are no separate source files and the problem does not exist. My experience with APL was not based on using the image (longterm), but with Smalltalk, there is a source file paired with the image (you can see this with both Squeak and Pharo). And Iceberg has been around for years allowing you to sel…

Definitely. Dyalog APL has support for source files, too, but I didn't want to overcomplicate things. The way that it saves environments (i.e. to a binary file or to a source file) is tangential to the point: that it has the ability to save the environment at all. REPLs go best with languages that can save their live environments, so that you don't have to worry about getting your work back out of the REPL environment. If your language can't save its environment, you need some other way to improve the REPL-to-source workflow.

Re: Why your REPL experience sucks

#22
post #11

Leaving wrong explanation for posterity and to keep thread understandable. See everything after [edit] for corrected explantion This is an important distinction between (funcall #'foo ...) and (funcall 'foo ...) in Common Lisp as well. #'foo evaluates to the function named by the symbol foo, while 'foo evaluates to the symbol foo itself. So if you redefine the function named by the symbol foo any already compiled for…

In sbcl those two work the same.

Re: Why your REPL experience sucks

#23

Earlier quoted context omitted.

This feels a little too strong to me. DrRacket has as its fundamental UI flow a combination of whole-reloaded-file and a REPL, and obviously the creators of Racket are intimately familiar with Lisp REPLs. One problem being addressed is the discrepancy between REPL-based development and non-image-based languages: you have to get your code back out into a source file somehow, without forgetting anything. In image-based…

> In image-based languages like Smalltalk and APL, you just save your environment after modifying it interactively; there are no separate source files and the problem does not exist. My experience with APL was not based on using the image (longterm), but with Smalltalk, there is a source file paired with the image (you can see this with both Squeak and Pharo). And Iceberg has been around for years allowing you to sel…

It's historically been pretty janky, though, and an acknowledged problem within the Smalltalk community. I think Pharo perfected their solution only within the past 10 years.

Re: Why your REPL experience sucks

#24
post #11

Leaving wrong explanation for posterity and to keep thread understandable. See everything after [edit] for corrected explantion This is an important distinction between (funcall #'foo ...) and (funcall 'foo ...) in Common Lisp as well. #'foo evaluates to the function named by the symbol foo, while 'foo evaluates to the symbol foo itself. So if you redefine the function named by the symbol foo any already compiled for…

In sbcl those two work the same.

They don't. What aidenn0 said applies to all CL implementations, although with a small correction that it's not "any already compiled forms", but rather any variables/objects that already hold the value of `#'foo` that will keep pointing to the old definition. Compiled code calling `(funcall #'foo ...)` will get the new definition normally (unless `foo` is inlined).

Re: Why your REPL experience sucks

#25
post #3

We keep on making fun of php, but it had this problem solved in the 90s. Hit F5 in the browser, get the latest version of your software. No waiting 30s for a rebuild, no stale versions in caches, no headaches.

Modern PHP can be like the REPL experience though depending on situation. Half the time the new code loads, the other half the time it serves from cache (opcode cache?). I'm not a PHP person so I'm probably doing something dumb, but I've found that inserting a `systemctl restart php-fpm.service nginx.server` into my test workflow avoids that problem.

Re: Why your REPL experience sucks

#26
post #11

Leaving wrong explanation for posterity and to keep thread understandable. See everything after [edit] for corrected explantion This is an important distinction between (funcall #'foo ...) and (funcall 'foo ...) in Common Lisp as well. #'foo evaluates to the function named by the symbol foo, while 'foo evaluates to the symbol foo itself. So if you redefine the function named by the symbol foo any already compiled for…

In sbcl those two work the same.

Derp, of course it does because (funcall #'foo) evaluates "#'foo" every time it runs. You need to bind (or assign) the value for it to actually show the difference:

    (defun bar () 3)
    
    (defvar *the-fn* #'bar)
    
    (defvar *the-sym* 'bar)
    
    (defun foo ()
      (funcall *the-fn*))
    
    (defun baz ()
      (funcall *the-sym*))
Then at the REPL:

    CL-USER> (values (baz) (foo))
    3
    3
    CL-USER> (defun bar () 2)
    WARNING: redefining COMMON-LISP-USER::BAR in DEFUN
    BAR
    CL-USER> (values (baz) (foo))
    2
    3

Re: Why your REPL experience sucks

#27
post #3

We keep on making fun of php, but it had this problem solved in the 90s. Hit F5 in the browser, get the latest version of your software. No waiting 30s for a rebuild, no stale versions in caches, no headaches.

These days we also have psysh.org which is a pretty sweet interactive PHP shell.

Doesn't have the sophisticated halting and rewinding and redefining that Common Lisp and other image based REPL:s do, but it allows for very rapid development in a similar way.

Re: Why your REPL experience sucks

#28
post #7

A related question is why would you want to reload the entire file/namespace every time you make a change? Coming from a Common Lisp background, I am used to evaluating single forms/functions. I would sometimes re-evaluate the entire namespace, but that would be relatively rare. I switched to Clojure many years ago and followed the same workflow. Then ClojureScript and tools like figwheel came along and I was somewha…

I have a shortcut, which reloads all modified files, then re-evaluates the previously evaluated expression, within the context of the same namespace it was evaluated in previously.

This way I can be in any file, modify any `def` of `defn` and see its effect on the expression I'm working on.

After modified something, I might jump to its unit test or jump to some definition of some adjacent vars, where i see a point which I want to probe (with a function like this: `(defn ? [x] (pprint x) x)`).

Then I have 2 modified files, and my cursor is in a 3rd file and the expression I'm iterating on is in a 4th; doesn't matter, I can just keep hitting the same shortcut (Cmd-Ctrl-Enter) to see the effects of my changes on the "expression-under-test".

It's an extremely convenient workflow, which is also easy to teach to ppl!

Downside is that you can't have namespaces, which have side-effects WHEN they are loaded.

To deal with that problem, I'm using `redelay`, `rmap`, `meta-merge` & `defonce`, eg:

    (ns service
     (:require
       [meta-merge.core :refer [meta-merge]]
       [gini.rmap :as rmap :refer [rmap ref] :rename {ref $}]
       [redelay.core :as redelay])

    (def kit
      (merge {:cfg {:db/uri "..."}}
             (rmap {:db (connect (:db/uri ($ :cfg)))})))

    (defonce service-ref
      (redelay/state
        :start (rmap/valuate! 
                 (meta-merge service/kit 
                             {:cfg  {:param 'override}
                              :some 'special-component}))))

    (defn service [] (deref service-ref))

    (comment
      (.close service-ref)
      (-> (service) (some-operation x y z))
      )
More info on this topic is here: https://functionalbytes.nl/clojure/rmap/2020/07/04/rmap-2-up...

Re: Why your REPL experience sucks

#29
I've only used a few REPLs so far but Ruby and Elixir are great (and I assume inspired by Lisp). I cut my teeth developing with REPLs and actually struggle a lot with trying to use language environments without them. Like, how can I just experiment and run a little one-of bit of code and data before starting the actual implementation? Or call and play around with the function I just wrote?

Re: Why your REPL experience sucks

#30

I've only used a few REPLs so far but Ruby and Elixir are great (and I assume inspired by Lisp). I cut my teeth developing with REPLs and actually struggle a lot with trying to use language environments without them. Like, how can I just experiment and run a little one-of bit of code and data before starting the actual implementation? Or call and play around with the function I just wrote?

Have a look at Fred0verflow's videos, for example:

* https://www.youtube.com/watch?v=St0_jsPnGAw - analyze some SO HTTP API responses * https://www.youtube.com/watch?v=TaazvSJvBaw - transducers from the ground up

I think they are great, crystal-clear examples of REPL-based development.

Post reply on HN