If by Haskell's REPL you mean GHCi, I think it probably qualifies, but it lacks one of the nicer features of a REPL, which is that you can type the same code interactively that you can load from a file. In fact that's how SLIME works with Lisp, by literally copying forms from a Lisp file into the interactive session. If you copy/paste Haskell code out of your .hs files into GHCi you get errors, because the syntax is slightly different, which I find a bit confusing & inconvenient.
Meh. You want to do imperative stuff in the repl, and you can't do that at the top-level in Haskell. The way ghci works is solid. When you are in the ghci repl, you are coding inside the do-notation for the IO monad.
Really, IMHO, jumping back and forth between the editor and the REPL with constant reloading is a better approach than pasting things into the REPL. I remember when I was playing with Common Lisp, I would always have trouble keeping the code on disk and the code loaded into the REPL synced.
>I remember when I was playing with Common Lisp, I would always have trouble keeping the code on disk and the code loaded into the REPL synced.
Then you're doing it wrong. Most people doesn't type directly into the REPL, they open up a separate buffer in Emacs and play in that. The REPL is just for small tests. There is no such thing as 'keeping the code synced'.
I don't know how you develop Lisp, but one style which seems encouraged by environments like SLIME is this: You write your file in emacs, and each time you finish a function definition you hit the "send definition to repl" keystroke, and then experiment a bit in the repl to see that you got it right.
If you do things this way, the state of the running lisp interpreter depends on the entire history of the coding session. Each time you add a new definition, you mutate the interpreter's memory. There is no guarantee that the current state matches what you would have if you recompiled the entire system from scratch.
On the other hand, the usual style in Haskell development is that you write a function definition and then hit the "reload" key combination, and this makes the state of the repl exactly the match the contents of the file. It throws away the results of any commands you ran in the repl in the meantime.
(This seems like an interesting cultural difference, something like "Haskell/ML/Java/Scheme programmers think of a program as a text, Common Lisp/Smalltalk programmers think of a program as an OS process").
One does that in Lisp, too. But often we want to avoid that. Lisp programs are often written in such a way that interactive modification is painless. There are a few very large Lisp programs which would take too long to compile/load each time. Thus we learn to deal with changing running programs. A Lisp system is often like a big collection of objects.
That's how I was doing it. It's been too long to remember the details, but I remember that there are different phases when code is loaded, and it can get very tricky when you're doing heavy meta-programming stuff. Everything works when load your new definitions from a buffer into the repl, but when you try to load it again from a file, all the phase issues come into play.
The main thing I use a REPL for is playing around with defining/redefining functions and then using them in various ways, and the different syntax plus the weird :{ :} business made that tedious in GHCi. So I ditched it and just moved to the old-school C-style "edit a file, save, compile, run" cycle.
With emacs and haskell-mode, I just open a Haskell buffer, and C-c C-l (control-c control-l). This will reload that buffer into another buffer that runs GHCi, creating it if it doesn't exist and using the existing one if it does.
Erlang has Distel, which is a pretty powerful SLIME like mode: https://github.com/massemanet/distel (and written by Luke Gorrie, the same guy who wrote SLIME).
I haven't used either Distel or SLIME extensively enough to say if it's comparable, but it looks fairly similar.
Well none of these languages have anything like Lisp's `read`, but Haskell's interactive loop is fairly usable and extensible. You can add vi-like keybindings and interact with other programs like hoogle, which lets you look up things based on types.
It doesn't have a similar error handling system to lisp or scheme no, but it does have an interactive debugger, and the code does get compiled when you're in ghci (including whatever modules you have loaded).
Oh please. Besides the fact, that CL has a ton of other features which set it apart any of mentioned langs, none of their REPLs are comparable in power. We have interactive debuggers built in and a language designed for it.
Take this simple example:
> (+ 1 "foo")
The value "foo" is not of the expected type NUMBER.
[Condition of type TYPE-ERROR]
Restarts:
0: [USE-VALUE] Use a new value of type NUMBER instead of "foo".
...
> 0<RET>41<RET>
=> 42
>