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 :load
ed 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.
Next: Enum ranges