Using the REPL

Haskell has a nice REPL known as GHCi, where GHC refers to the main Haskell compiler and the ‘i’ is for interactive. GHCi and the continual typechecking tool called ghcid are useful for fast feedback loops while you code, but GHCi has a lot of other features besides. This is a very basic introduction to some of those.

You open a new GHCi session with the ghci command. You can load the file you’re working on as you open a REPL session by passing the filename as an argument to the ghci command.

$ ghci branching.hs
GHCi, version 8.6.4:...
[1 of 1] Compiling Main             ( branching.hs, interpreted )
Ok, one module loaded.

You can also load a file after GHCi is open by using the :load command, or :l for short, with a filename as an argument.

$ ghci
GHCi, version 8.6.4...

λ> :load branching.hs
[1 of 1] Compiling Main             ( branching.hs, interpreted )
Ok, one module loaded.

As you make changes, use :reload or :r. This doesn’t take a filename argument. It reloads whatever was last loaded, whether that was one file or a whole project’s worth of files.

λ> :reload
Ok, one module loaded.

λ> :r
Ok, one module loaded.

To check what the last file(s) you :loaded is, use :show modules.

λ> :show modules
Main             ( branching.hs, interpreted )

Any imports that a file lists are also available once the file is loaded into GHCi. However, you can also import modules directly into the REPL, for exploration and experimentation, if the package is available. Use import along with the module’s name. Use :show imports to see what has been imported.

λ> :show imports
import Prelude -- implicit

Modules of base such as Data.Char are always available.

Many functions and types available in base, such as isSpace from Data.Char, are not included in Prelude.

λ> :type isSpace

<interactive>:1:1: error: Variable not in scope: isSpace

λ> import Data.Char

λ> :type isSpace
isSpace :: Char -> Bool

Libraries that GHC has a dependency on, such as the time library, are also available.

λ> import Data.Time

λ> :show imports
import Data.Time
import Prelude -- implicit

Packages from your project’s dependencies or previous installations using cabal install or stack install may also be available. You can check what packages are available to your local GHC before you open a GHCi session. Any of the modules from any of the packages in that list can be imported directly in GHCi.

$ ghc-pkg list

You can declare values at the REPL prompt as well as evaluate functions, on those values or others. The Prelude module of the standard library (called base) is always in scope by default in GHCi sessions, so all those functions and types are available for use.

λ> nameList = ["Melman", "Alonzo", "Snowcone"]

λ> length nameList
3

λ> map length nameList
[6,6,8]

Functions can also be declared directly at the prompt. Here f is the name given to the combination of map and length used above.

λ> f list = map length list
f :: Foldable t => [t a] -> [Int]

λ> f nameList
[6,6,8]

You can also declare custom types in the REPL. Notice the similarity in style of the declarations.

λ> data ServicePlan = Free | Monthly | Annual deriving Show
data ServicePlan = ...

Declaring a constant value.

Declaring a function.

Declaring a type.

λ> nameList = ["Melman", "Alonzo", "Snowcone"]

λ> f list = map length list

λ> data ServicePlan = Free | Monthly | Annual

There is special syntax for multiline expressions in the REPL. This example uses the default multiline syntax while preserving the layout of the code as it would appear in a text file.

λ> :{
 > billAmount plan =
 >   case plan of
 >     Free -> 0
 >     Monthly -> 5
 >     Annual -> billAmount Monthly * 12
 > :}
billAmount :: Num p => ServicePlan -> p

λ> billAmount Monthly
5

Some find this style of multiline syntax more convenient. It must be enabled using the :set +m command. This command automatically turns on multiline syntax when it recognizes that the line ends in a block-opening keyword, such as of or do.

Note that this example uses curly braces and semicolons instead of indentation, although indentation would have worked just as in the previous example.

Multiline syntax is most useful when copying and pasting code into the REPL. Otherwise, it’s usually better to type into a file and :load it.

λ> :set +m

λ> billAmount plan = case plan of
 > { Free -> 0;
 > Monthly -> 5;
 > Annual -> billAmount Monthly * 12}
billAmount :: Num p => ServicePlan -> p

The :type or :t command gives the type information for the specified value or function. It can give type information for any value or function in scope, whether it comes from the Prelude, a dependency, or declarations made directly in GHCi.

λ> :type nameList
nameList :: [[Char]]

λ> :type billAmount
billAmount :: Num p => ServicePlan -> p

λ> :type map
map :: (a -> b) -> [a] -> [b]

λ> :type True
True :: Bool

λ> :type Monthly
Monthly :: ServicePlan

But :type can’t give you type information about types themselves.

λ> :type Bool

<interactive>:1:1: error: Data constructor not in scope: Bool

For that you may want :info.

λ> :info Bool
data Bool = False | True 	-- Defined in `GHC.Types'
instance Eq Bool -- Defined in `GHC.Classes'
instance Ord Bool -- Defined in `GHC.Classes'
instance Show Bool -- Defined in `GHC.Show'
instance Read Bool -- Defined in `GHC.Read'
instance Enum Bool -- Defined in `GHC.Enum'
instance Bounded Bool -- Defined in `GHC.Enum'

The :info command can also give information about functions and operators.

λ> :info map
map :: (a -> b) -> [a] -> [b] 	-- Defined in `GHC.Base'

With infix operators, the :info command helpfully tells you the precedence and fixity of the operator. infixl indicates left associativity, while the number after that indicates its precedence.

λ> :info (+)
class Num a where
  (+) :: a -> a -> a
  ...
  	-- Defined in `GHC.Num'
infixl 6 +

Quit a GHCi session using the :quit or :q command.

λ> :quit
Leaving GHCi.