1

How could I do a reload and then evaluate last expression inside GHCI?

By last expression I mean the last thing you typed in GHCI which was not a GHCI command. For example: 42, "I am an expression", etc.

Example usage, let's say the reload and then evaluate last expression GHCI command is :x:

>>> 42
42
>>> :x
42
>>>

In example above :x does a reload and then evaluates 42 again.

Is there any GHCI command that evaluates last expression?

If there was some GHCI command that evaluates last expression, let's say it was :last, I could have done the reload and evaluate last expression by adding following to .ghci:

:def re const $ return $ Data.List.unlines [":reload", ":last"]

In the guide it says that : does Repeat the previous command.

https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html

But I tried that in cabal repl and it did not work. Just prints a list of macros:

Răzvan Flavius Panda
  • 21,730
  • 17
  • 111
  • 169
  • 1
    You can use the arrows (up and down) to move back in the history. – Willem Van Onsem Apr 15 '20 at 20:07
  • 1
    @WillemVanOnsem I know that, and it is not a solution to my problem. – Răzvan Flavius Panda Apr 15 '20 at 20:11
  • Well it repeats the previous command (so another line that starts with `":"`). An expression like `putStrLn "a"` is not a command, it is an expression. If you thus write `:r`, then it will reload the file, if you then hit `:` again, then it will reload once again. – Willem Van Onsem Apr 15 '20 at 20:15
  • @WillemVanOnsem In the question I am asking about one GHCI command which when run will in order: 1. reload 2. run the previous thing I run in GHCI – Răzvan Flavius Panda Apr 15 '20 at 20:18
  • 1
    With respect to `:`, by "repeat the previous command" the Guide means the previous GHCi command. If you do `:t foo` immediately followed by `: bar`, GHCi will print the type of `bar`. – duplode Apr 15 '20 at 20:25
  • @duplode I see, was actually considering that it might mean that but ruled it out since it did not actually do what it says it does: run previous command. It does work the way you mentioned. I think in the documentation it should be clarified how it actually works. – Răzvan Flavius Panda Apr 15 '20 at 20:28
  • This function is a feature of the Atom package [IDE-Haskell-REPL](https://atom-haskell.github.io/extra-packages/ide-haskell-repl/), and since it's [implemented in typescript](https://github.com/atom-haskell/ide-haskell-repl/blob/7b1777d3ef54fdeedcd6a1e18af7e6c5b87a663d/src/ide-haskell-repl-base.ts#L200), I suppose it must mean that there is no such feature in GHCi. See alse the related [feature request](https://github.com/atom-haskell/ide-haskell-repl/issues/27). – MikaelF Apr 15 '20 at 23:02
  • @MikaelF Yes, I am actually using Atom with that feature, that is why I am asking how to do the same thing under GHCI, cabal repl, stack repl, etc. It's weird that no-one asked this question until now. – Răzvan Flavius Panda Apr 15 '20 at 23:36

1 Answers1

1

I wasn't able to find any description of this functionality in GHCi, and the fact that this is implemented using a wrapper arount GHCi in the Atom package IDE-Haskell-REPL leads me to believe that this is because GHCi can't do it on its own. GHCi also doesn't allow other processes to access its history during a session, since its history is only saved to a file (~/.ghc/ghci_history on unix) at the end of a session, so scripting a GHCi session is tricky (but see below).

I think the closest you can get is by starting GHCi with a wrapper script using expect, have it listen to a specific keypress sequence, and then send the ":r enter up up enter" keys for you:

#!/usr/bin/env expect

log_user 0
spawn ghci
log_user 1

while 1 {
  interact "##" {send ":r\r\[A\[A\r"} #see note
  exit
}

This way, whenever you hit ##, GHCi will reload and run/evaluate whatever command or expression is first in the history. This is a admittedly a crude solution, and it might not be the safest, but I wanted to at least come up with an alternative.

Note: it looks like the control characters in the code block above don't render correctly, so you might have to get them from the markdown source of this answer, or from the output of the autoexpect command.

MikaelF
  • 3,518
  • 4
  • 20
  • 33
  • Is it possible to set a shortcut such as `Ctrl + R`? – Răzvan Flavius Panda Apr 17 '20 at 07:57
  • Ctrl+R is already used in GHCi (it searches history, just like in bash), but you can get control codes for any key sequence using autoexpect. Enter autoexpect, type the keys you want, then type "exit" and the results will be in a file in the current directory (script.exp IIRC). – MikaelF Apr 17 '20 at 14:20
  • 1
    I really like the idea of using `Ctrl + something`, since it solves one of the issues of my answer, that it prevents the first expected character from being echoed until it can see that the second character isn't the second expected character. Just don't override an existing key combination, or you'll never be able to use it again. By the way, almost every Ctrl combination is [bound to something](https://ss64.com/bash/syntax-keyboard.html) in `bash`. Another improvement would be using a special character such as `¾`, but availability of characters will depend on keyboard mappings. – MikaelF Apr 17 '20 at 16:22
  • `almost every Ctrl combination is bound to something in bash` isn't the problem whether the character is bound in GHCI and not bash? – Răzvan Flavius Panda Apr 17 '20 at 19:31
  • Both can be a problem, but i'm not aware of any other Ctrl combination being mapped in GHCi (maybe I'm wrong). `expect` will prevent anything from reaching bash or GHCi. For example, try mapping `Ctrl+t` (switch the two characters before the cursor) and using `Ctrl+t` in ghci with and without `expect`. In the end, mapping such combinations aren't a problem if you don't plan on using them, it's just something to be aware of. Especially `Ctrl+r`, since it can save you a lot of time retyping long commands/expressions. – MikaelF Apr 17 '20 at 19:37