Hacker News new | ask | show | jobs
by gumby 3220 days ago
I am not convinced that immutability matters; this seems like a bias. After all the original REPL, and the name itself, was in Lisp (note that the first Lisp implementations were not interactive, but it was the first interactive language) and Lisp doesn't have immutable data structures.

(READ, EVAL, and PRINT are all old Lisp primitives, and the REPL was literally implemented with them. There's also a complex macro called LOOP but it was added much later, and is not integral to the language)

3 comments

Author here. I could be wrong of course, but this stems from my experience in using a REPL from JavaScript. I hit 2 sorts of hurdle with mutability there: 1- I wasn't sure what I was viewing in the REPL was what the code of interest was perceiving, could have been changed afterwards and 2- immutable values were more suited for exploratory work due to their speculative nature, e.g I could try a function calls with variants of a data structure without 'committing' to them. Maybe I had a bad REPL experience in JS and blamed it on immutability; OTOH, maybe other people had a great REPL experience in Common Lips for other reasons, and attribute it to mutability. This certainly makes me want to give a new try to Common Lisp :)
I'd even say immutability can be a disadvantage.

I normally only use a repl when I don't know what I'm doing.

My normal process with a repl in an immutable language goes like this:

1. Assign something to a name

2. Realise that was wrong, try again.

3. Get told no. Remember to tell the repl to forget the wrong one and do it again (or choose a new name).

4. Try to assign something else to another name (probably 'foo')

5. Realise that I already used foo about an hour ago in a different experiment, and hadn't closed the repl in between.

Immutable is great for actual programming. Less so for quick experiments.

I think there may be some confusion here, immutability is about the stability of values (e.g. I have a string, list, map etc and I want to pass it to others, view its value, make changes without affecting the original value).

Immutability is not the same as rebinding a name (in clojure at least) e.g. the following is perfectly valid (global and local binding of the same name repeatedly)

  (def a 1)
  (def a 2)
  (def a 3)
  (println a)
  => 3

  (let [a 4
        a 5
        a 6]
     (println a))
  => 6
I see. When I see "immutable", I think of Prolog and Erlang (e.g. once X is 6, X can't be matched to 5 in the same scope) rather than languages that simply prefer byval over byref.
well clojure does pass by reference, but the reference is to something that cannot be changed (i.e. a persistent data structure)
Unification and assignment are very different concepts, though, with only fleeting similarity in some cases.
Mildly off-topic: automatically shadowing bindings is one of my favorite changes elixir made to erlang
Some languages have immutable bindings too, where you only shadow previous bindings.
If you can shadow previous bindings then that would also cause no problem for repl re-usage, unless the shadowing syntax is different to the binding syntax.

Either way, unrelated to the point in the article about the advantage of immutable values for repl, not immutable bindings.

I would certainly agree that truly immutable bindings would be at best quite a stumbling block for repls, but so far no one was advocating for them as a good repl feature.

> so far no one was advocating for them as a good repl feature

GHCi has this feature, if you explicitly turn it on (-fwarn-name-shadowning with -Werror). I agree that it's not a terribly useful way to run a REPL.

For a global def to be a "rebinding" rather than assignment is crippling. It means that previous references to a still see the old thing, which is wrong if you want them to see the new thing.
If you want others to see the new thing automatically, you need something much more powerful than plain old assignment anyway: something which will let you pick a synchronization strategy.

"Changes at any arbitrary time, including when you are halfway through reading it" is not in any way a sound synchronization strategy and the only thing you get with assignment.

You seem to understand the important difference between identity (what you see when you read an object) and reference (what you use to access the object). Next important thing on the list is how "reference" cannot just be a pointer to a place in memory -- unless it is immutable.

> you need something much more powerful than plain old assignment anyway: something which will let you pick a synchronization strategy.

Right, just like the king's subjects don't actually need a toaster, but rather breakfast food cooker.

Clojure does not rebind the reference, it just changes it to point to something else. So it does not suffer from what you describe. Previous references will see the new thing as you'd hope.

So def on an existing binding does assignment. That's what is meant by "re-binding".

Indeed, by immutability I really meant immutability of the values, but not of the name bindings. So I would say an optimal setup is to have a mutable execution environment which handles immutable values.
> Lisp doesn't have immutable data structures

Racket does have immutable data structures and Dr. Racket has a good REPL IMHO.

http://beautifulracket.com/explainer/data-structures.html

That's the converse of my point. The article claimed immutable data is important for a good REPL and I said that the claim was an overstatement. I didn't say that immutable data makes a REPL impossible.

I quite like immutable datastructures, as it happens.

I was replying that LISP doesn't have immutable data structures.
Common Lisp has some immutable data structure such as, oh, numbers. If you arithmetically encode a record of data into a bignum integer, that is immutable.