Difference between revisions of "GHC/GHCi"

From HaskellWiki
< GHC
Jump to navigation Jump to search
m (Added link for .ghci file location.)
m (Fixed incorrect usage of \STX (which may have caused problems with line wrapping))
 
(7 intermediate revisions by 6 users not shown)
Line 1: Line 1:
 
[[Category:GHC|GHCi]]
 
[[Category:GHC|GHCi]]
== Introduction ==
 
   
  +
= Introduction =
GHCi is GHC's interactive environment, in which Haskell expressions can be interactively evaluated and programs can be interpreted. The [http://www.haskell.org/ghc/docs/latest/html/users_guide/index.html GHC User's Guide] contains [http://www.haskell.org/ghc/docs/latest/html/users_guide/ghci-debugger.html more fundamental and detailed information about GHCi.]
 
   
  +
GHCi is GHC's interactive environment, in which Haskell expressions can be interactively evaluated and programs can be interpreted. Before reading this, read the [https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html GHCi section] of the [https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ GHC User's Guide].
This page is a place to collect advice about how to use GHCi beyond what User's Guide covers. Please add to it!
 
   
  +
This page is a place to collect advice and snippets for use with the latest version of GHCi, beyond what the User's Guide covers. Please add to it!
== Advanced customization ==
 
   
  +
= Invoking GHCi =
=== Using <tt>.ghci</tt>, a mini-tutorial ===
 
   
  +
GHCi can be run in a number of ways, depending on your setup and requirements:
There is a lot more one can do to customize and extend GHCi. Some extended examples can be found in an email posted to <tt>haskell-cafe</tt>, titled
 
[http://www.haskell.org/pipermail/haskell-cafe/2007-September/032260.html getting more out of ghci]. Dating from September 2007, and using GHC 6.6.1, some of the GHCi tickets mentioned in there have since been fixed, but the message should still serve as a useful introduction to writing your own <tt>.ghci</tt> files. It also provides several useful commands you might want to copy into your own file!-) Newer GHCis support the multiline commands mentioned in the message, allowing for more readable <tt>.ghci</tt> files (at the time, definitions had to be squashed into single lines, so you have to read the message to understand the `.ghci` file). For those still using older GHCis, a variant file for 6.4.1 is available, too:
 
   
  +
# standalone: <tt>ghci</tt>
* [http://www.haskell.org/pipermail/haskell-cafe/2007-September/032260.html "getting more out of ghci", the mini-tutorial]
 
  +
# within the stack global project: <tt>stack repl</tt>
* [http://www.cs.kent.ac.uk/people/staff/cr3/toolbox/haskell/dot-squashed.ghci squashed .ghci, for 6.6.1 or later]
 
  +
# within a specific stack project: <tt>cd project; stack repl</tt>
* [http://www.cs.kent.ac.uk/people/staff/cr3/toolbox/haskell/dot-squashed.ghci641 squashed .ghci, for 6.4.1]
 
  +
# within a specific stack project, but including <tt>GHC_PACKAGE_PATH</tt>: <tt>cd project; stack exec ghci</tt>
  +
# within a temporary "fake" cabal project: <tt>cabal new-repl</tt>
  +
# within a specific cabal project: <tt>cd project; cabal new-repl</tt>
   
  +
= Customisation =
(See the user guide for [http://www.haskell.org/ghc/docs/6.12.2/html/users_guide/ghci-dot-files.html where to locate the <tt>.ghci</tt> file]).
 
   
  +
When invoked, GHCi tries to load a startup script. The [https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html#the-ghci-files documentation] has the best description of where it tries to find these files. In general, the snippets on this page can be added there to enable the desired features by default. This page will assume you are using <tt>~/.ghci</tt>, so adjust as necessary if you are using a different file.
=== Customized GHCi interactive environments ===
 
   
  +
When running GHCi from within a project, another startup script may also be specified by stack, which unfortunately can cause some of your startup script's customisations to be reset. Also, when running from cabal or stack, not all modules will be available within your startup script. Therefore, it is better to avoid using any extra modules unless you know you are running a standalone GHCi session. As such, it is recommended to use a different startup file for standalone sessions, by putting the following in your <tt>.bashrc</tt> (or equivalent for other shells):
You can create shell commands that start up GHCi and
 
  +
alias ghci='ghci -v0 -ignore-dot-ghci -ghci-script ~/.ghci.standalone'
initialize it for use as a specialized interactive
 
computing environment for any purpose that you can
 
imagine.
 
   
  +
This will make GHCi load the <tt>~/.ghci.standalone</tt> startup file instead, and there you can be free to load and use modules that you know are available in that environment. The <tt>-v0</tt> will also ensure that GHCi is not as verbose as the default settings make it. Throughout this page, it is noted when a snippet requires modules that are not generally available in all environments.
The idea is that if you put the following lines in your
 
<tt>.ghci</tt> file, GHCi will load commands at startup
 
from whatever file whose path you specify in
 
the <tt>GHCIRC</tt> environment variable. You can then easily write
 
shell scripts that exploit this to initialize GHCi in
 
any manner you please.
 
   
  +
GHCi reads its input through a library called [https://hackage.haskell.org/package/haskeline haskeline], which can also be [https://github.com/judah/haskeline/wiki/UserPreferences customized]. A typical <tt>~/.haskeline</tt> file might look like this:
<haskell>
 
  +
maxHistorySize: Nothing
-- Read GHCI commands from the file whose name is
 
  +
historyDuplicates: IgnoreConsecutive
-- in the GHCIRC environment variable
 
  +
completionPromptLimit: Just 250
:def _load const(System.Environment.getEnvironment>>=maybe(return"")readFile.lookup"GHCIRC")
 
  +
This will give you unlimited history, will omit history entries that are identical to the previous entry, and when using tab-completion, prompt only when the number of completions exceeds 250.
:_load
 
:undef _load
 
</haskell>
 
   
  +
To use Vi-like keybindings (similar to Bash's <tt>set -o vi</tt>) add also the following line:
=== External tool integration ===
 
  +
editMode: Vi
==== Hoogle ====
 
External command-line tools like [[Hoogle]] can be integrated in GHCi by adding a line to .ghci similar to
 
<haskell>
 
:def hoogle \str -> return $ ":! hoogle --count=15 \"" ++ str ++ "\""
 
</haskell>
 
   
  +
= The Snippet Library =
Make sure that the directory containing the executable is in your PATH environment variable or modify the line to point directly to the executable. Invoke the executable with commands like
 
<haskell>
 
:hoogle map
 
</haskell>
 
   
  +
The following is the recommended basis for the <tt>.ghci</tt> file:
==== Hlint ====
 
  +
<haskell style="background:transparent">
Hlint can be similarly integrated. It is much more complex, however, as one must acquire the filename to run hlint on:
 
  +
-- Turn off output for resource usage and types. This is to reduce verbosity when reloading this file.
  +
:unset +s +t
  +
-- Turn on multi-line input and remove the distracting verbosity.
  +
:set +m -v0
  +
-- Turn off all compiler warnings and turn on OverloadedStrings for interactive input.
  +
:seti -w -XOverloadedStrings
  +
-- Set the preferred editor for use with the :e command. I would recommend using an editor in a separate terminal, and using :r to reload, but :e can still be useful for quick edits from within GHCi.
  +
:set editor vim
   
  +
...
<haskell>
 
  +
-- rest of file
-- <http://www.cs.kent.ac.uk/people/staff/cr3/toolbox/haskell/dot-squashed.ghci641>
 
  +
...
let { redir varcmd = case break Data.Char.isSpace varcmd of { (var,_:cmd) -> return $ unlines [":set -fno-print-bind-result","tmp <- System.Directory.getTemporaryDirectory","(f,h) <- System.IO.openTempFile tmp \"ghci\"","sto <- GHC.Handle.hDuplicate System.IO.stdout","GHC.Handle.hDuplicateTo h System.IO.stdout","System.IO.hClose h",cmd,"GHC.Handle.hDuplicateTo sto System.IO.stdout","let readFileNow f = readFile f >>= \\t->length t `seq` return t",var++" <- readFileNow f","System.Directory.removeFile f"]; _ -> return "putStrLn \"usage: :redir <var> <cmd>\"" } }
 
:def redir redir
 
-- End copied material
 
   
  +
-- Use :rr to reload this file.
-- Integration with the hlint code style tool
 
  +
:def! rr \_ -> return ":script ~/.ghci"
let hlint _ = return $ unlines [":set -w", ":redir hlintvar1 :show modules", ":cmd return (\":! hlint \" ++ (concat $ Data.List.intersperse \" \" (map (fst . break (==',') . drop 2 . snd . break (== '(')) $ lines hlintvar1)))", ":set -Wall"]
 
  +
-- Turn on output of types. This line should be last.
:def hlint hlint
 
  +
:set +t
 
</haskell>
 
</haskell>
   
  +
== Fancy Prompts ==
(There may be a more up-to-date version in the hlint darcs repo.)
 
   
  +
Both of the following snippets use the Haskell logo as the prompt, but this must be supported by your terminal font. Under Linux, this is probably already the case, but on Mac, this can be achieved by installing [https://github.com/ryanoasis/nerd-fonts Nerd Fonts]. This is easily done using [https://brew.sh/ brew]:
=== Package and documentation lookup ===
 
  +
brew tap caskroom/fonts
  +
brew cask install font-hack-nerd-font
   
  +
Instead, to use a lambda for the prompt, change the "\xe61f" in the snippet to "λ".
Ever tried to find the users guide for the version of GHCi you are currently running? Or information about the packages installed for it? The new <tt>[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/ghc-paths ghc-paths]</tt> package makes such tasks easier by exporting a <tt>GHC.Paths</tt> module:
 
  +
<haskell>
 
  +
This snippet requires the <tt>directory</tt> module to configure a nice prompt:
Prelude> :browse GHC.Paths
 
  +
<haskell style="background:transparent">
docdir :: FilePath
 
  +
:{
ghc :: FilePath
 
  +
:set -package directory
ghc_pkg :: FilePath
 
  +
dotGHCI_myPrompt promptString ms _ = do
libdir :: FilePath
 
  +
-- Get the current directory, replacing $HOME with a '~'.
  +
pwd <- getpwd
  +
-- Determine which is the main module.
  +
let main_module = head' [ m' | (m:m') <- ms, m == '*' ]
  +
-- Put together the final prompt string.
  +
-- ANSI escape sequences allow for displaying colours in compatible terminals. See [http://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html this guide] for help interpreting them.
  +
return $ concat [ "\ESC[33m\STX", pwd, main_module, "\ESC[37m\STX", promptString, " \ESC[0m\STX" ]
  +
where
  +
head' (x:_) = " \ESC[38;5;227m\STX" ++ x
  +
head' _ = ""
  +
getpwd = getpwd' <$> (System.Environment.getEnv "HOME") <*> System.Directory.getCurrentDirectory
  +
getpwd' home pwd = if zipWith const pwd home == home
  +
then '~':drop (length home) pwd
  +
else pwd
  +
:}
  +
:set prompt-function dotGHCI_myPrompt "\ESC[38;5;129m\STX\xe61f"
  +
:set prompt-cont-function dotGHCI_myPrompt "∷"
 
</haskell>
 
</haskell>
  +
We can define some auxiliary commands to make this more comfortable:
 
  +
The following snippet works without loading extra modules, but requires a [https://en.wikipedia.org/wiki/POSIX POSIX] environment.
<haskell>
 
  +
<haskell style="background:transparent">
:ghc_pkg cmds -- run ghc-pkg commands
 
  +
:{
:browser url -- start browser with url
 
  +
dotGHCI_myPrompt promptString ms _ = do
:doc [relative] -- open docs, with optional relative path
 
  +
-- Get the current directory, replacing $HOME with a '~'.
:users_guide [relative] -- open users guide, with optional relative path
 
  +
pwd <- getpwd
  +
-- Determine which is the main module.
  +
let main_module = head' [ m' | (m:m') <- ms, m == '*' ]
  +
-- Put together the final prompt string.
  +
-- ANSI escape sequences allow for displaying colours in compatible terminals. See [http://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html this guide] for help interpreting them.
  +
return $ concat [ "\ESC[33m\STX", pwd, main_module, "\ESC[37m\STX", promptString, " \ESC[0m\STX" ]
  +
where
  +
head' (x:_) = " \ESC[38;5;227m\STX" ++ x
  +
head' _ = ""
  +
getpwd = getpwd' <$> System.Environment.getEnv "HOME" <*> System.Posix.getWorkingDirectory
  +
getpwd' home pwd = if zipWith const pwd home == home
  +
then '~':drop (length home) pwd
  +
else pwd
  +
:}
  +
:set prompt-function dotGHCI_myPrompt "\ESC[38;5;129m\STX\xe61f"
  +
:set prompt-cont-function dotGHCI_myPrompt "∷"
 
</haskell>
 
</haskell>
So, <haskell>:ghc_pkg list</haskell> will list the packages for the current GHCi instance, <haskell>:ghc_pkg find-module Text.Regex</haskell> will tell us what package that module is in, etc. <haskell>:doc</haskell> will open a browser window on the documentation for this GHCi version, <haskell>:doc /Cabal/index.html</haskell> takes us to the Cabal docs, and <haskell>:users_guide /flag-reference.html</haskell> takes us to the flag reference, all matching the version of GHCi we're in, provided that the docs and <tt>ghc-paths</tt> are installed.
 
   
  +
And here's what this should look like:
Here are the definitions - adapt to your preferences (note that the construction of the documentation path from <tt>libdir</tt> and <tt>docdir</tt> is slightly dodgy):
 
  +
<span style="color:orange">~</span>$ cd src/gitit
<haskell>
 
  +
<span style="color:orange">~/src/gitit</span>$ ghci src/Network/Gitit.hs
:def ghc_pkg (\l->return $ ":!"++GHC.Paths.ghc_pkg++" "++l)
 
  +
<span style="color:orange">~/src/gitit</span> <span style="color:#e0e030; font-weight:800">Network.Gitit</span><span style="color:#b860ff; font-weight:1000">'''λ'''</span> :info wiki
  +
wiki :: Config -> ServerPart Response
  +
-- Defined at src/Network/Gitit.hs:133:1
  +
<span style="color:orange">~/src/gitit</span> <span style="color:#e0e030; font-weight:800">Network.Gitit</span><span style="color:#b860ff; font-weight:1000">'''λ'''</span>
   
  +
== Pretty Printing ==
:def browser (\l->return $ ":!c:/Progra~1/Opera/Opera.exe "++l)
 
   
  +
GHCi's [https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html#using-a-custom-interactive-printing-function <tt>-interactive-print</tt> option] allows for interactive output to be piped through a pretty printer. Here is a snippet, with custom colours:
let doc p = return $ ":browser "++GHC.Paths.libdir++dropWhile (/='/')GHC.Paths.docdir++relative where { relative | p=="" = "/index.html" | otherwise = p }
 
  +
<haskell style="background:transparent">
:def doc doc
 
  +
-- Colourise ghci output (use :nopretty to disable)
  +
-- Required libraries: pretty-show hscolour
  +
:set -package pretty-show -package hscolour
  +
import qualified Language.Haskell.HsColour as HSC
  +
import qualified Language.Haskell.HsColour.Colourise as HSC
  +
:{
  +
dotGHCI_myPrint :: (Show a) => a -> IO ()
  +
dotGHCI_myPrint a = putStrLn $ HSC.hscolour HSC.TTY myColourPrefs False False "" False $ Text.Show.Pretty.ppShow a
  +
where
  +
myColourPrefs = HSC.defaultColourPrefs -- { HSC.conop = [HSC.Foreground HSC.Yellow]
  +
-- , HSC.conid = [HSC.Foreground HSC.Yellow, HSC.Bold]
  +
-- , HSC.string = [HSC.Foreground $ HSC.Rgb 29 193 57]
  +
-- , HSC.char = [HSC.Foreground HSC.Cyan]
  +
-- , HSC.number = [HSC.Foreground $ HSC.Rgb 202 170 236]
  +
-- , HSC.keyglyph = [HSC.Foreground HSC.Yellow]
  +
-- }
  +
:}
  +
:seti -interactive-print dotGHCI_myPrint
  +
:def! pretty \_ -> return ":set -interactive-print dotGHCI_myPrint"
  +
:def! nopretty \_ -> return ":set -interactive-print System.IO.print"
  +
:m -Language.Haskell.HsColour
  +
:m -Language.Haskell.HsColour.Colourise
  +
</haskell>
   
  +
The following snippet works without loading extra modules, but requires the <tt>ppsh</tt> and <tt>HsColour</tt> binaries (from [https://hackage.haskell.org/package/pretty-show <tt>pretty-show</tt>] and [https://hackage.haskell.org/package/hscolour <tt>hscolour</tt>]) to be installed in your PATH.
let users_guide p = doc ("/users_guide"++if null p then "/index.html" else p)
 
  +
<haskell style="background:transparent">
:def users_guide users_guide
 
  +
-- Colourise ghci output (use :nopretty to disable)
  +
:{
  +
:def! pretty \_ -> return $ unlines [
  +
":unset +t",
  +
"pp x = putStrLn =<< catch' (rp \"HsColour\" []) =<< catch' (rp \"ppsh\" []) (show x) where { rp = System.Process.readProcess; catch' f x = Control.Exception.catch (f x) (h x); h :: String -> Control.Exception.SomeException -> IO String; h x _ = return x }",
  +
":seti -interactive-print pp",
  +
":set +t"
  +
]
  +
:}
  +
:def! nopretty \_ -> return ":set -interactive-print System.IO.print"
  +
:pretty
  +
:unset +t
 
</haskell>
 
</haskell>
   
  +
After adding all that, you should have a slightly nicer output from GHCi:
=== GHCi on Acid ===
 
  +
<span style="color:orange">~</span><span style="color:#b860ff; font-weight:1000">'''λ'''</span> [1,2,3]
  +
<span style="color:red">[</span> <span style="color:#caaaec">1</span> , <span style="color:#caaaec">2</span> , <span style="color:#caaaec">3</span> <span style="color:red">]</span>
  +
it :: Num a => [a]
   
  +
== Extra Commands ==
GHCi on Acid is an extension to GHCi (Interactive GHC) for adding useful lambdabot features. It does pretty much anything lambadabot does, just nicely embedded inside your GHCi.
 
   
  +
There are many extra utilities for GHCi. The most versatile way to access them is by invoking external binaries from definition bindings. For example, we can add an <tt>ls</tt> command by simply using the operating system's <tt>ls</tt>:
==== Features ====
 
  +
<haskell style="background:transparent">
  +
:def! ls \s -> return $ ":!ls " ++ s
  +
</haskell>
   
  +
And you use these definitions by simply calling them with a colon, just like the builtin GHCi commands:
Here are some examples of the commands that can be used.
 
  +
<span style="color:orange">~</span><span style="color:#b860ff; font-weight:1000">'''λ'''</span> :ls -1adF
  +
./
  +
../
  +
example.hs
   
  +
=== HLint and Hoogle ===
The :instances command shows all the instances of a class:
 
   
  +
Similarly, after installing the [http://hackage.haskell.org/package/hoogle <tt>hoogle</tt>] and [http://hackage.haskell.org/package/hlint <tt>hlint</tt>] binaries:
GOA> :instances Monad
 
  +
<haskell style="background:transparent">
((->) r), ArrowMonad a, Cont r, ContT r m, Either e, ErrorT e m, IO, Maybe, RWS r w s, RWST r w s m, Reader r, ReaderT r m, ST s, State s, StateT s m, Writer w, WriterT w m, []
 
  +
dotGHCI_escapeShellArg arg = "'" ++ concatMap (\c -> if c == '\'' then "'\\''" else [c]) arg ++ "'"
GOA> :instances Arrow
 
  +
:def! hoogle return . (":!hoogle -q --count=15 --color " ++) . dotGHCI_escapeShellArg
(->), Kleisli m
 
  +
:def! search return . (":!hoogle -q --count=3 --color " ++) . dotGHCI_escapeShellArg
GOA> :instances Num
 
  +
:def! doc return . (":!hoogle -q --color --info " ++) . dotGHCI_escapeShellArg
Double, Float, Int, Integer
 
  +
:def! hlint \s -> return $ ":!hlint " ++ if null s then "." else s
  +
</haskell>
   
  +
This allows you to do the following from within GHCi:
Here we have the :hoogle command, for querying the Hoogle database. Great for looking for functions of a specific type:
 
  +
<span style="color:orange">~</span><span style="color:#b860ff; font-weight:1000">'''λ'''</span> :hlint
  +
No hints
  +
<span style="color:orange">~</span><span style="color:#b860ff; font-weight:1000">'''λ'''</span> :hoogle is:exact Data.Set.insert
  +
Data.Set insert :: Ord a => a -> Set a -> Set a
  +
Data.Set.Internal insert :: Ord a => a -> Set a -> Set a
  +
Data.SetMap insert :: (Ord k, Ord a) => k -> a -> SetMap k a -> SetMap k a
  +
Data.Set.NonEmpty insert :: Ord a => a -> NESet a -> NESet a
  +
<span style="color:orange">~</span><span style="color:#b860ff; font-weight:1000">'''λ'''</span> :search is:exact <$>
  +
Prelude (<$>) :: Functor f => (a -> b) -> f a -> f b
  +
Control.Applicative (<$>) :: Functor f => (a -> b) -> f a -> f b
  +
Data.Functor (<$>) :: Functor f => (a -> b) -> f a -> f b
  +
<span style="color:orange">~</span><span style="color:#b860ff; font-weight:1000">'''λ'''</span> :doc <*>
  +
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
  +
base Prelude
  +
Sequential application.
  +
  +
A few functors support an implementation of <*> that is
  +
more efficient than the default one.
   
  +
=== Lambdabot Commands ===
GOA> :hoogle Arrow
 
Control.Arrow :: module
 
Control.Arrow.Arrow :: class Arrow a
 
Control.Arrow.ArrowZero :: class Arrow a => ArrowZero a
 
GOA> :hoogle b -> (a -> b) -> Maybe a -> b
 
Prelude.maybe :: b -> (a -> b) -> Maybe a -> b
 
Data.Maybe.maybe :: b -> (a -> b) -> Maybe a -> b
 
   
  +
There is an IRC bot called [http://hackage.haskell.org/package/lambdabot <tt>lambdabot</tt>] which includes many tools which can be accessed directly in "offline" mode. Here are some example usages:
The :source command gives a link to the source code of a module (sometimes you are curious):
 
  +
<span style="color:orange">~</span><span style="color:#b860ff; font-weight:1000">'''λ'''</span> :pl \x y -> x + 1 -- converts code to point-free (aka pointless) form
  +
const . (1 +)
  +
<span style="color:orange">~</span><span style="color:#b860ff; font-weight:1000">'''λ'''</span> :unpl const . (1 +) -- converts back from point-free (aka pointless) form
  +
(\ x _ -> 1 + x)
  +
<span style="color:orange">~</span><span style="color:#b860ff; font-weight:1000">'''λ'''</span> :do getLine >>= putStrLn -- converts binds to do notation
  +
do { a <- getLine; putStrLn a}
  +
<span style="color:orange">~</span><span style="color:#b860ff; font-weight:1000">'''λ'''</span> :undo do { a <- getLine; putStrLn a } -- converts do blocks to bind notation
  +
getLine >>= \ a -> putStrLn a
  +
<span style="color:orange">~</span><span style="color:#b860ff; font-weight:1000">'''λ'''</span> :index <<^ -- finds the module that defines the given identifier
  +
Control.Arrow
  +
<span style="color:orange">~</span><span style="color:#b860ff; font-weight:1000">'''λ'''</span> :instances Arrow -- finds all instances of a given type class
  +
(->), Kleisli m
  +
<span style="color:orange">~</span><span style="color:#b860ff; font-weight:1000">'''λ'''</span> :src <<^ -- tries to find the source code for the given identifier
  +
a <<^ f = a <<< arr f
  +
<span style="color:orange">~</span><span style="color:#b860ff; font-weight:1000">'''λ'''</span> :oeis 3 5 8 13 -- looks up the On-Line Encyclopedia of Integer Sequences (https://oeis.org/)
  +
https://oeis.org/A000045 Fibonacci numbers: F(n) = F(n-1) + F(n-2) with F(0) = 0 and F(1) = 1.
  +
[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269,2178309,3524578,5702887,9227465,14930352,24157817,
  +
...
   
  +
And here's the snippet which allows for all of that:
GOA> :source Data.Maybe
 
  +
<haskell style="background:transparent">
http://darcs.haskell.org/packages/base/Data/Maybe.hs
 
  +
dotGHCI_escapeShellArg arg = "'" ++ concatMap (\c -> if c == '\'' then "'\\''" else [c]) arg ++ "'"
  +
lb s1 s2 = return $ ":!lambdabot -n -e " ++ dotGHCI_escapeShellArg s1 ++ "\\ " ++ dotGHCI_escapeShellArg s2
  +
:def! lb lb "" -- runs arbitrary lambdabot commands
  +
:def! pl lb "pl" -- converts code to point-free (aka pointless) form
  +
:def! unpl lb "unpl" -- converts back from point-free (aka pointless) form
  +
:def! do lb "do" -- converts binds to do notation
  +
:def! undo lb "undo" -- converts do blocks to bind notation
  +
:def! index lb "index" -- finds the module that defines the given identifier
  +
:def! instances lb "instances" -- finds all instances of a given type class
  +
:def! src lb "src" -- tries to find the source code for the given identifier
  +
:def! oeis lb "oeis" -- looks up the On-Line Encyclopedia of Integer Sequences (https://oeis.org/)
  +
</haskell>
   
  +
Another method to achieve the same thing is to import <tt>GOA</tt> and use the <tt>lambdabot</tt> function. However, using the <tt>lambdabot</tt> binary directly is simpler and works without importing extra modules that are not related to your project.
Similarly, :docs gives a link to the documentation of a module.
 
   
  +
=== More Package and Documentation Lookup Commands ===
GOA> :docs Data.Maybe
 
http://haskell.org/ghc/docs/latest/html/libraries/base/Data-Maybe.html
 
   
  +
This snippet requires the ghc-paths module, and allows us to call <tt>ghc-pkg</tt> from GHCi:
The :index command is a nice way to search modules.
 
  +
<haskell style="background:transparent">
  +
:set -package ghc-paths
  +
import GHC.Paths
  +
:def! ghc_pkg (\s -> return $ ":!" ++ GHC.Paths.ghc_pkg ++ " " ++ s)
  +
:m -GHC.Paths
  +
</haskell>
   
  +
For example:
GOA> :index Monad
 
  +
<span style="color:orange">~</span><span style="color:#b860ff; font-weight:1000">'''λ'''</span> :ghc_pkg describe ghc-paths
Control.Monad, Prelude, Control.Monad.Reader, Control.Monad.Writer, Control.Monad.State, Control.Monad.RWS, Control.Monad.Identity, Control.Monad.Cont, Control.Monad.Error, Control.Monad.List
 
  +
name: ghc-paths
  +
version: 0.1.0.9
  +
id: ghc-paths-0.1.0.9-AeY5FiD7eih3ZffF6P7kJ1
  +
key: ghc-paths-0.1.0.9-AeY5FiD7eih3ZffF6P7kJ1
  +
license: BSD-3
  +
copyright: (c) Simon Marlow
  +
maintainer: Simon Marlow <marlowsd@gmail.com>
  +
author: Simon Marlow
  +
stability: stable
  +
synopsis: Knowledge of GHC's installation directories
  +
description:
  +
Knowledge of GHC's installation directories
  +
category: Development
  +
...
   
  +
The following snippet requires the ghc-paths module, and creates the <tt>:docs</tt> command to look up the documentation for a given identifier:
Then we have :pl, which shows the pointless (or: point-free) way of writing a function, which is very useful for learning and sometimes for fun:
 
  +
<haskell style="background:transparent">
  +
:set -package ghc-paths
  +
import GHC.Paths
  +
:{
  +
dotGHCI_escapeShellHTMLArg arg = "'" ++ concatMap (\c -> case c of
  +
'\'' -> "'\\''"
  +
'>' -> "&gt;"
  +
'<' -> "&lt;"
  +
'&' -> "&amp;"
  +
_ -> [c]) arg ++ "'"
  +
:}
  +
docs s = return $ ":!echo file://" ++ GHC.Paths.docdir ++ "/../$(tr \\< \\\\n < " ++ GHC.Paths.docdir ++ "/../doc-index-All.html | grep -A100 -F \\>" ++ dotGHCI_escapeShellHTMLArg s ++ " | grep href | head -1 | cut -d\\\" -f2)"
  +
:def! docs docs
  +
:m -GHC.Paths
  +
</haskell>
   
  +
It can be used like such:
GOA> :pl (\x -> x * x)
 
  +
<span style="color:orange">~</span><span style="color:#b860ff; font-weight:1000">'''λ'''</span> :docs ***
join (*)
 
  +
file:///Users/pdr/.stack/programs/x86_64-osx/ghc-8.6.4/share/doc/ghc-8.6.4/html/libraries/base-4.12.0.0/../base-4.12.0.0/Control-Arrow.html#v:-42--42--42-
GOA> :pl (\x y -> (x * 5) + (y * 5))
 
(. (5 *)) . (+) . (5 *)
 
   
==== How to install ====
+
== Older GHCi Versions ==
   
  +
There are many other snippets out there, but a lot of those have since been replaced by built-in functionality and/or no longer work under stack and cabal. Here is a list of how to achieve the same functionality that some old snippets used to provide:
$ cabal install lamdabot
 
  +
* [https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html#ghc-flag--ghci-script <tt>-ghci-script</tt>] replaces loading further startup scripts via an environment variable or other means
$ cabal install goa
 
  +
* Pressing Ctrl-L clears the screen
  +
* <tt>:script</tt> sources other startup scripts
   
  +
= Frequently Asked Questions (FAQ) =
Then edit your .ghci to look like the following:
 
   
  +
== How do I get GHCi to print the type of a function instead of an error? ==
:m - Prelude
 
:m + GOA
 
setLambdabotHome "/home/chris/.cabal/bin"
 
:def bs lambdabot "botsnack"
 
:def pl lambdabot "pl"
 
:def unpl lambdabot "unpl"
 
:def redo lambdabot "redo"
 
:def undo lambdabot "undo"
 
:def index lambdabot "index"
 
:def docs lambdabot "docs"
 
:def instances lambdabot "instances"
 
:def hoogle lambdabot "hoogle"
 
:def source lambdabot "fptools"
 
:def where lambdabot "where"
 
:def version lambdabot "version"
 
:def src lambdabot "src"
 
   
  +
There's no easy way to do this in general, but if you use <tt>:set +t</tt> as recommended above, you can simply create a <tt>Show</tt> instance for functions, which will output nothing. However, this won't work if GHCi can't work out what concrete types your function needs, and print an error anyway. In any case, this snippet will do that:
And you should be able to do this:
 
   
  +
<haskell style="background:transparent">
chris@chrisamilo:~$ ghci
 
  +
instance Show (a -> b) where show _ = ""
GHCi, version 6.10.4: http://www.haskell.org/ghc/ :? for help
 
  +
:set +t
Loading package ghc-prim ... linking ... done.
 
  +
</haskell>
Loading package integer ... linking ... done.
 
Loading package base ... linking ... done.
 
Loading package syb ... linking ... done.
 
Loading package base-3.0.3.1 ... linking ... done.
 
Loading package old-locale-1.0.0.1 ... linking ... done.
 
Loading package old-time-1.0.0.2 ... linking ... done.
 
Loading package filepath-1.1.0.2 ... linking ... done.
 
Loading package unix-2.3.2.0 ... linking ... done.
 
Loading package directory-1.0.0.3 ... linking ... done.
 
Loading package process-1.0.1.1 ... linking ... done.
 
Loading package goa-3.0.2 ... linking ... done.
 
GOA> :src foldr
 
src foldr
 
foldr f z [] = z
 
foldr f z (x:xs) = f x (foldr f z xs)
 
GOA>
 
   
  +
And here it is in action:
Tip: if you accidentally unload the GOA module, use :m + GOA to load it.
 
  +
<span style="color:orange">~</span><span style="color:#b860ff; font-weight:1000">'''λ'''</span> map
  +
  +
it :: (a -> b) -> [a] -> [b]
  +
However, this simple trick doesn't work for everything:
  +
<span style="color:orange">~</span><span style="color:#b860ff; font-weight:1000">'''λ'''</span> (<*>)
  +
  +
<interactive>:14:1: error:
  +
• Ambiguous type variable ‘f0’ arising from a use of ‘it’
  +
prevents the constraint ‘(Applicative f0)’ from being solved.
  +
Probable fix: use a type annotation to specify what ‘f0’ should be.
  +
These potential instances exist:
  +
instance Arrow a => Applicative (ArrowMonad a)
  +
-- Defined in ‘Control.Arrow’
  +
instance Applicative (Either e) -- Defined in ‘Data.Either’
  +
instance Applicative IO -- Defined in ‘GHC.Base’
  +
...plus three others
  +
...plus 19 instances involving out-of-scope types
  +
(use -fprint-potential-instances to see them all)
  +
• In the first argument of ‘print’, namely ‘it’
  +
In a stmt of an interactive GHCi command: print it
   
  +
== How do I stop GHCi from printing the result of a bind statement? ==
== Frequently Asked Questions (FAQ) ==
 
   
  +
Sometimes you want to perform an IO action at the prompt that will produce a lot of data (e.g. reading a large file). When you try to do this, GHCi will helpfully spew this data all over your terminal, making the console temporarily unavailable.
=== How do I stop GHCi from printing the result of a bind statement? ===
 
 
Sometimes you want to perform an IO action at the prompt that will produce a lot of data (e.g. reading a large file). When you try to do this, GHCi will helpfully spew this data all over your terminal, making the console temporarily unavailable.
 
   
 
To prevent this, use <tt>:set -fno-print-bind-result</tt>. If you want this option to be permanently set, add it to your <tt>.ghci</tt> file.
 
To prevent this, use <tt>:set -fno-print-bind-result</tt>. If you want this option to be permanently set, add it to your <tt>.ghci</tt> file.
 
== Additional command advice ==
 
 
=== The :def command ===
 
 
The [http://www.haskell.org/ghc/docs/latest/html/users_guide/ghci-commands.html#id2902944 :def command], documented here, allows quite GHCi's commands to be extended in quite a powerful way.
 
 
Here is one example.
 
 
Prelude> let loop = do { l <- getLine; if l == "\^D" then return () else do appendFile "foo.hs" (l++"\n"); loop }
 
Prelude> :def pasteCode (\_ -> loop >> return ":load foo.hs")
 
 
This defines a new command :pasteCode, which allows you to paste Haskell code directly into GHCi. You type the command :pasteCode, followed by the code you want, followed by ^D, followed (unfortunately) by enter, and your code is executed. Thus:
 
 
Prelude> :pasteCode
 
x = 42
 
^D
 
Compiling Main ( foo.hs, interpreted )
 
Ok, modules loaded: Main.
 
*Main> x
 
42
 
*Main>
 
 
== Compatibility/shell/platform integration ==
 
 
=== Readline/editline ===
 
 
There are some tricks to getting readline/editline to work as expected with GHCi.
 
 
==== A readline-aware GHCi on Windows ====
 
 
Mauricio reports: I've just uploaded a package (<tt>rlwrap</tt>) to Cygwin that I like to use with <tt>ghci</tt>. You can use it like this:
 
<pre>
 
rlwrap ghcii.sh
 
</pre>
 
and then you will use <tt>ghc</tt> as if it were readline aware (i.e., you can
 
press up arrow to get last typed lines etc.). <tt>rlwrap</tt> is very stable
 
and I never had unexpected results while using it.
 
 
Since the issue of <tt>ghci</tt> integration with terminals has been raised
 
here sometimes, I thought some guys here would be interested (actually,
 
I found rlwrap looking for a better way to use ghci).
 
 
==== rlwrap (for GHCI compiled without readline/editline) ====
 
 
GHCi has support for session-specific command-line completion, but only if it was built with the readline or editline package, and some versions of GHCi aren't. In such cases, you can try [http://utopia.knoware.nl/~hlub/uck/rlwrap/ rlwrap (readline wrapper)] to attach readline "from the outside", which isn't as specific, but gives basic completion support. In particular, there's an rlwrap package for Cygwin.
 
 
For starters,
 
 
<code>
 
rlwrap -cr ghci
 
</code>
 
 
gives you filename completion, and completion wrt to previous input/output in your GHCi session (so if a GHCi error message suggests to set <hask>AnnoyinglyLongVerySpecificOption</hask>, that will be available for completion;-).
 
 
If you want to get more specific, you need to supply files with possible completions - flags and modules spring to mind, but where to get those?
 
 
1. extracting a list of options from the flag-reference in the users guide:
 
 
<pre>
 
cat /cygdrive/c/ghc/ghc-6.9.20080514/doc/users_guide/flag-reference.html
 
| sed 's/</\n</g'
 
| sed '/<code class="option">/!d;s/<code class="option">\(.*\)$/\1/'
 
> options.txt
 
</pre>
 
 
actually, we only want the dynamic or :setable options, and no duplicates, so:
 
 
<pre>
 
cat /cygdrive/c/ghc/ghc-6.9.20080514/doc/users_guide/flag-reference.html
 
| sed 's/<tr/\n<tr/g'
 
| grep '<code class="option".*>\(dynamic\|:set\)<'
 
| sed 's/^.*<code class="option">\([^<]*\)<.*$/\1/'
 
| uniq
 
> options.txt
 
</pre>
 
 
2. extracting a list of modules from <tt>ghc-pkg</tt>:
 
 
<code>
 
ghc-pkg field '*' exposed-modules | sed 's/exposed-modules: //; s/^\s\+//g' >modules.txt
 
</code>
 
 
And now,
 
 
<code>
 
rlwrap -cr -f modules.txt -f options.txt ghcii.sh
 
</code>
 
 
will give you completion wrt filenames, options, module names, and previous session contents, as well as the usual readline goodies, like history search and editing. The main drawback is that the completion is neither session nor context-specific, so it will suggest filenames where module names are expected, it will suggest module names that may not be exposed, etc.
 

Latest revision as of 12:03, 15 May 2020


Introduction

GHCi is GHC's interactive environment, in which Haskell expressions can be interactively evaluated and programs can be interpreted. Before reading this, read the GHCi section of the GHC User's Guide.

This page is a place to collect advice and snippets for use with the latest version of GHCi, beyond what the User's Guide covers. Please add to it!

Invoking GHCi

GHCi can be run in a number of ways, depending on your setup and requirements:

  1. standalone: ghci
  2. within the stack global project: stack repl
  3. within a specific stack project: cd project; stack repl
  4. within a specific stack project, but including GHC_PACKAGE_PATH: cd project; stack exec ghci
  5. within a temporary "fake" cabal project: cabal new-repl
  6. within a specific cabal project: cd project; cabal new-repl

Customisation

When invoked, GHCi tries to load a startup script. The documentation has the best description of where it tries to find these files. In general, the snippets on this page can be added there to enable the desired features by default. This page will assume you are using ~/.ghci, so adjust as necessary if you are using a different file.

When running GHCi from within a project, another startup script may also be specified by stack, which unfortunately can cause some of your startup script's customisations to be reset. Also, when running from cabal or stack, not all modules will be available within your startup script. Therefore, it is better to avoid using any extra modules unless you know you are running a standalone GHCi session. As such, it is recommended to use a different startup file for standalone sessions, by putting the following in your .bashrc (or equivalent for other shells):

alias ghci='ghci -v0 -ignore-dot-ghci -ghci-script ~/.ghci.standalone'

This will make GHCi load the ~/.ghci.standalone startup file instead, and there you can be free to load and use modules that you know are available in that environment. The -v0 will also ensure that GHCi is not as verbose as the default settings make it. Throughout this page, it is noted when a snippet requires modules that are not generally available in all environments.

GHCi reads its input through a library called haskeline, which can also be customized. A typical ~/.haskeline file might look like this:

maxHistorySize: Nothing
historyDuplicates: IgnoreConsecutive
completionPromptLimit: Just 250

This will give you unlimited history, will omit history entries that are identical to the previous entry, and when using tab-completion, prompt only when the number of completions exceeds 250.

To use Vi-like keybindings (similar to Bash's set -o vi) add also the following line:

editMode: Vi

The Snippet Library

The following is the recommended basis for the .ghci file:

-- Turn off output for resource usage and types.  This is to reduce verbosity when reloading this file.
:unset +s +t
-- Turn on multi-line input and remove the distracting verbosity.
:set +m -v0
-- Turn off all compiler warnings and turn on OverloadedStrings for interactive input.
:seti -w -XOverloadedStrings
-- Set the preferred editor for use with the :e command.  I would recommend using an editor in a separate terminal, and using :r to reload, but :e can still be useful for quick edits from within GHCi.
:set editor vim

...
-- rest of file
...

-- Use :rr to reload this file.
:def! rr \_ -> return ":script ~/.ghci"
-- Turn on output of types.  This line should be last.
:set +t

Fancy Prompts

Both of the following snippets use the Haskell logo as the prompt, but this must be supported by your terminal font. Under Linux, this is probably already the case, but on Mac, this can be achieved by installing Nerd Fonts. This is easily done using brew:

brew tap caskroom/fonts
brew cask install font-hack-nerd-font

Instead, to use a lambda for the prompt, change the "\xe61f" in the snippet to "λ".

This snippet requires the directory module to configure a nice prompt:

:{
:set -package directory
dotGHCI_myPrompt promptString ms _ = do
  -- Get the current directory, replacing $HOME with a '~'.
  pwd <- getpwd
  -- Determine which is the main module.
  let main_module = head' [ m' | (m:m') <- ms, m == '*' ]
  -- Put together the final prompt string.
  -- ANSI escape sequences allow for displaying colours in compatible terminals.  See [http://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html this guide] for help interpreting them.
  return $ concat [ "\ESC[33m\STX", pwd, main_module, "\ESC[37m\STX", promptString, " \ESC[0m\STX" ]
  where
    head' (x:_) = " \ESC[38;5;227m\STX" ++ x
    head' _     = ""
    getpwd = getpwd' <$> (System.Environment.getEnv "HOME") <*> System.Directory.getCurrentDirectory
    getpwd' home pwd = if zipWith const pwd home == home
                         then '~':drop (length home) pwd
                         else pwd
:}
:set prompt-function dotGHCI_myPrompt "\ESC[38;5;129m\STX\xe61f"
:set prompt-cont-function dotGHCI_myPrompt "∷"

The following snippet works without loading extra modules, but requires a POSIX environment.

:{
dotGHCI_myPrompt promptString ms _ = do
  -- Get the current directory, replacing $HOME with a '~'.
  pwd <- getpwd
  -- Determine which is the main module.
  let main_module = head' [ m' | (m:m') <- ms, m == '*' ]
  -- Put together the final prompt string.
  -- ANSI escape sequences allow for displaying colours in compatible terminals.  See [http://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html this guide] for help interpreting them.
  return $ concat [ "\ESC[33m\STX", pwd, main_module, "\ESC[37m\STX", promptString, " \ESC[0m\STX" ]
  where
    head' (x:_) = " \ESC[38;5;227m\STX" ++ x
    head' _     = ""
    getpwd = getpwd' <$> System.Environment.getEnv "HOME" <*> System.Posix.getWorkingDirectory
    getpwd' home pwd = if zipWith const pwd home == home
                         then '~':drop (length home) pwd
                         else pwd
:}
:set prompt-function dotGHCI_myPrompt "\ESC[38;5;129m\STX\xe61f"
:set prompt-cont-function dotGHCI_myPrompt "∷"

And here's what this should look like:

~$ cd src/gitit
~/src/gitit$ ghci src/Network/Gitit.hs
~/src/gitit Network.Gititλ :info wiki
wiki :: Config -> ServerPart Response
  	-- Defined at src/Network/Gitit.hs:133:1
~/src/gitit Network.Gititλ 

Pretty Printing

GHCi's -interactive-print option allows for interactive output to be piped through a pretty printer. Here is a snippet, with custom colours:

-- Colourise ghci output (use :nopretty to disable)
-- Required libraries: pretty-show hscolour
:set -package pretty-show -package hscolour
import qualified Language.Haskell.HsColour as HSC
import qualified Language.Haskell.HsColour.Colourise as HSC
:{
dotGHCI_myPrint :: (Show a) => a -> IO ()
dotGHCI_myPrint a = putStrLn $ HSC.hscolour HSC.TTY myColourPrefs False False "" False $ Text.Show.Pretty.ppShow a
  where
    myColourPrefs = HSC.defaultColourPrefs -- { HSC.conop    = [HSC.Foreground HSC.Yellow]
                                           -- , HSC.conid    = [HSC.Foreground HSC.Yellow, HSC.Bold]
                                           -- , HSC.string   = [HSC.Foreground $ HSC.Rgb 29 193 57]
                                           -- , HSC.char     = [HSC.Foreground HSC.Cyan]
                                           -- , HSC.number   = [HSC.Foreground $ HSC.Rgb 202 170 236]
                                           -- , HSC.keyglyph = [HSC.Foreground HSC.Yellow]
                                           -- }
:}
:seti -interactive-print dotGHCI_myPrint
:def! pretty \_ -> return ":set -interactive-print dotGHCI_myPrint"
:def! nopretty \_ -> return ":set -interactive-print System.IO.print"
:m -Language.Haskell.HsColour
:m -Language.Haskell.HsColour.Colourise

The following snippet works without loading extra modules, but requires the ppsh and HsColour binaries (from pretty-show and hscolour) to be installed in your PATH.

-- Colourise ghci output (use :nopretty to disable)
:{
:def! pretty \_ -> return $ unlines [
  ":unset +t",
  "pp x = putStrLn =<< catch' (rp \"HsColour\" []) =<< catch' (rp \"ppsh\" []) (show x) where { rp = System.Process.readProcess; catch' f x = Control.Exception.catch (f x) (h x); h :: String -> Control.Exception.SomeException -> IO String; h x _ = return x }",
  ":seti -interactive-print pp",
  ":set +t"
  ]
:}
:def! nopretty \_ -> return ":set -interactive-print System.IO.print"
:pretty
:unset +t

After adding all that, you should have a slightly nicer output from GHCi:

~λ [1,2,3]
[ 1 , 2 , 3 ]
it :: Num a => [a]

Extra Commands

There are many extra utilities for GHCi. The most versatile way to access them is by invoking external binaries from definition bindings. For example, we can add an ls command by simply using the operating system's ls:

:def! ls \s -> return $ ":!ls " ++ s

And you use these definitions by simply calling them with a colon, just like the builtin GHCi commands:

~λ :ls -1adF
./
../
example.hs

HLint and Hoogle

Similarly, after installing the hoogle and hlint binaries:

dotGHCI_escapeShellArg arg = "'" ++ concatMap (\c -> if c == '\'' then "'\\''" else [c]) arg ++ "'"
:def! hoogle return . (":!hoogle -q --count=15 --color " ++) . dotGHCI_escapeShellArg
:def! search return . (":!hoogle -q --count=3 --color " ++) . dotGHCI_escapeShellArg
:def! doc return . (":!hoogle -q --color --info " ++) . dotGHCI_escapeShellArg
:def! hlint \s -> return $ ":!hlint " ++ if null s then "." else s

This allows you to do the following from within GHCi:

~λ :hlint
No hints
~λ :hoogle is:exact Data.Set.insert
Data.Set insert :: Ord a => a -> Set a -> Set a
Data.Set.Internal insert :: Ord a => a -> Set a -> Set a
Data.SetMap insert :: (Ord k, Ord a) => k -> a -> SetMap k a -> SetMap k a
Data.Set.NonEmpty insert :: Ord a => a -> NESet a -> NESet a
~λ :search is:exact <$>
Prelude (<$>) :: Functor f => (a -> b) -> f a -> f b
Control.Applicative (<$>) :: Functor f => (a -> b) -> f a -> f b
Data.Functor (<$>) :: Functor f => (a -> b) -> f a -> f b
~λ :doc <*>
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
base Prelude
Sequential application.

A few functors support an implementation of <*> that is
more efficient than the default one.

Lambdabot Commands

There is an IRC bot called lambdabot which includes many tools which can be accessed directly in "offline" mode. Here are some example usages:

~λ :pl \x y -> x + 1                     -- converts code to point-free (aka pointless) form
const . (1 +)
~λ :unpl const . (1 +)                   -- converts back from point-free (aka pointless) form
(\ x _ -> 1 + x)
~λ :do getLine >>= putStrLn              -- converts binds to do notation
do { a <- getLine; putStrLn a}
~λ :undo do { a <- getLine; putStrLn a } -- converts do blocks to bind notation
getLine >>= \ a -> putStrLn a
~λ :index <<^                            -- finds the module that defines the given identifier
Control.Arrow
~λ :instances Arrow                      -- finds all instances of a given type class
(->), Kleisli m
~λ :src <<^                              -- tries to find the source code for the given identifier
a <<^ f = a <<< arr f
~λ :oeis 3 5 8 13                        -- looks up the On-Line Encyclopedia of Integer Sequences (https://oeis.org/)
https://oeis.org/A000045 Fibonacci numbers: F(n) = F(n-1) + F(n-2) with F(0) = 0 and F(1) = 1.
[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269,2178309,3524578,5702887,9227465,14930352,24157817,
...

And here's the snippet which allows for all of that:

dotGHCI_escapeShellArg arg = "'" ++ concatMap (\c -> if c == '\'' then "'\\''" else [c]) arg ++ "'"
lb s1 s2 = return $ ":!lambdabot -n -e " ++ dotGHCI_escapeShellArg s1 ++ "\\ " ++ dotGHCI_escapeShellArg s2
:def! lb lb ""                   -- runs arbitrary lambdabot commands
:def! pl lb "pl"                 -- converts code to point-free (aka pointless) form
:def! unpl lb "unpl"             -- converts back from point-free (aka pointless) form
:def! do lb "do"                 -- converts binds to do notation
:def! undo lb "undo"             -- converts do blocks to bind notation
:def! index lb "index"           -- finds the module that defines the given identifier
:def! instances lb "instances"   -- finds all instances of a given type class
:def! src lb "src"               -- tries to find the source code for the given identifier
:def! oeis lb "oeis"             -- looks up the On-Line Encyclopedia of Integer Sequences (https://oeis.org/)

Another method to achieve the same thing is to import GOA and use the lambdabot function. However, using the lambdabot binary directly is simpler and works without importing extra modules that are not related to your project.

More Package and Documentation Lookup Commands

This snippet requires the ghc-paths module, and allows us to call ghc-pkg from GHCi:

:set -package ghc-paths
import GHC.Paths
:def! ghc_pkg (\s -> return $ ":!" ++ GHC.Paths.ghc_pkg ++ " " ++ s)
:m -GHC.Paths

For example:

~λ :ghc_pkg describe ghc-paths
name: ghc-paths
version: 0.1.0.9
id: ghc-paths-0.1.0.9-AeY5FiD7eih3ZffF6P7kJ1
key: ghc-paths-0.1.0.9-AeY5FiD7eih3ZffF6P7kJ1
license: BSD-3
copyright: (c) Simon Marlow
maintainer: Simon Marlow <marlowsd@gmail.com>
author: Simon Marlow
stability: stable
synopsis: Knowledge of GHC's installation directories
description:
    Knowledge of GHC's installation directories
category: Development
...

The following snippet requires the ghc-paths module, and creates the :docs command to look up the documentation for a given identifier:

:set -package ghc-paths
import GHC.Paths
:{
dotGHCI_escapeShellHTMLArg arg = "'" ++ concatMap (\c -> case c of
                                                         '\'' -> "'\\''"
                                                         '>' -> "&gt;"
                                                         '<' -> "&lt;"
                                                         '&' -> "&amp;"
                                                         _    -> [c]) arg ++ "'"
:}
docs s = return $ ":!echo file://" ++ GHC.Paths.docdir ++ "/../$(tr \\< \\\\n < " ++ GHC.Paths.docdir ++ "/../doc-index-All.html | grep -A100 -F \\>" ++ dotGHCI_escapeShellHTMLArg s ++ " | grep href | head -1 | cut -d\\\" -f2)"
:def! docs docs
:m -GHC.Paths

It can be used like such:

~λ :docs  ***
file:///Users/pdr/.stack/programs/x86_64-osx/ghc-8.6.4/share/doc/ghc-8.6.4/html/libraries/base-4.12.0.0/../base-4.12.0.0/Control-Arrow.html#v:-42--42--42-

Older GHCi Versions

There are many other snippets out there, but a lot of those have since been replaced by built-in functionality and/or no longer work under stack and cabal. Here is a list of how to achieve the same functionality that some old snippets used to provide:

  • -ghci-script replaces loading further startup scripts via an environment variable or other means
  • Pressing Ctrl-L clears the screen
  • :script sources other startup scripts

Frequently Asked Questions (FAQ)

How do I get GHCi to print the type of a function instead of an error?

There's no easy way to do this in general, but if you use :set +t as recommended above, you can simply create a Show instance for functions, which will output nothing. However, this won't work if GHCi can't work out what concrete types your function needs, and print an error anyway. In any case, this snippet will do that:

instance Show (a -> b) where show _ = ""
:set +t

And here it is in action:

~λ map

it :: (a -> b) -> [a] -> [b]

However, this simple trick doesn't work for everything:

~λ (<*>)

<interactive>:14:1: error:
    • Ambiguous type variable ‘f0’ arising from a use of ‘it’
      prevents the constraint ‘(Applicative f0)’ from being solved.
      Probable fix: use a type annotation to specify what ‘f0’ should be.
      These potential instances exist:
        instance Arrow a => Applicative (ArrowMonad a)
          -- Defined in ‘Control.Arrow’
        instance Applicative (Either e) -- Defined in ‘Data.Either’
        instance Applicative IO -- Defined in ‘GHC.Base’
        ...plus three others
        ...plus 19 instances involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    • In the first argument of ‘print’, namely ‘it’
      In a stmt of an interactive GHCi command: print it

How do I stop GHCi from printing the result of a bind statement?

Sometimes you want to perform an IO action at the prompt that will produce a lot of data (e.g. reading a large file). When you try to do this, GHCi will helpfully spew this data all over your terminal, making the console temporarily unavailable.

To prevent this, use :set -fno-print-bind-result. If you want this option to be permanently set, add it to your .ghci file.