Core Haskell tools
- 2021-08-14 -In this guide we'll take a look at a few core tools that are installed
with the Haskell toolchain. Specifically, ghc
, runghc
and ghci
.
These tools can be used to compile, interpret or explore Haskell programs.
If you haven't installed a Haskell toolchain yet, jump over to the haskell.org/downloads page.
Note: if you installed your Haskell toolchain using the stack tool only,
and these programs (ghc
, ghci
, runghc
) are not available in your terminal,
prefix the commands with stack exec --
. So <command>
becomes stack exec -- <command>
.
See the documentation in the
Stack user guide
for more details.
First, let's start by opening your system's command line interface
and running ghc --version
to make sure we have successfully
installed a Haskell toolchain:
➜ ghc --version
The Glorious Glasgow Haskell Compilation System, version 9.0.1
If this fails, consult the downloads page for information on how to install Haskell on your computer.
Compiling programs with ghc
Running ghc
invokes the Glasgow Haskell Compiler (GHC), and can be used to
compile Haskell modules and programs into native executables and libraries.
Create a new Haskell source file named hello.hs
,
and write the following code in it:
main = putStrLn "Hello, Haskell!"
Now, we can compile the program by invoking ghc
with the file name:
➜ ghc hello.hs
[1 of 1] Compiling Main ( hello.hs, hello.o )
Linking hello ...
GHC created the following files:
hello.hi
- Haskell interface file, we won't discuss this file in this articlehello.o
- Object file, the output of the compiler before linking, we won't discuss this file in this article either.hello
(orhello.exe
on Microsoft Windows) - A native runnable executable.
GHC will produce an executable when the source file satisfies both conditions:
- Defines the
main
function in the source file - Defines the module name to be
Main
(this can be done by addingmodule Main where
at the top of the file), or does not have a module declaration (which is then inferred as the moduleMain
).
Otherwise, it will only produce the .o
and .hi
files.
In our case, we have defined main
and omitted the module declaration,
so GHC created an executable for us. And we can run it:
➜ ./hello
Hello, Haskell!
Alternatively, we can skip the compilation phase by using the command runghc
:
➜ runghc hello.hs
Hello, Haskell!
runghc
interprets the source file instead of compiling it and does not
create build artifacts. This makes it very useful when developing programs
and can help accelerate the feedback loop. More information about runghc
can be found in the
GHC user guide.
Common options for GHC
Here are a few notable options we can use with ghc
and runghc
.
Since we already successfully compiled our hello.hs
program and
produced an executable, We'll have to use the flag -fforce-recomp
to force recompilation of our hello.hs
source file.
Otherwise GHC will notice that the content of hello.hs
hasn't changed
and will skip the recompilation.
Warnings
The -Wall
flag will enable GHC to emit many warnings about our code
(but not all warnings available, contrary to its name).
I strongly recommend always using it.
Let's compile our hello.hs
program again, this time with -Wall
:
➜ ghc -Wall hello.hs -fforce-recomp
[1 of 1] Compiling Main ( hello.hs, hello.o )
hello.hs:1:1: warning: [-Wmissing-signatures]
Top-level binding with no type signature: main :: IO ()
|
1 | main = putStrLn "Hello, Haskell!"
| ^^^^
Linking hello ...
GHC has successfully compiled our program, but it has also emitted a warning about
not annotating main
with a type signature. While Haskell can infer
the types of most expressions, it is recommended that top-level definitions
are annotated with their types.
We can remedy that by adding the following line above our main definition:
main :: IO ()
Now our hello.hs
source file looks like this:
main :: IO ()
main = putStrLn "Hello, world!"
And now GHC will compile hello.hs
without warnings.
-Wall
emits many useful warnings that can easily be bugs,
including warnings about name shadowing, unused variables, and more.
Note that -Wall
does not stop GHC from compiling your program.
If this is something you'd like to do, specifying the flag -Werror
will convert warnings to errors and will halt compilation when a warning
is emitted.
Optimisations
Another very useful flag is -O
. This flag asks GHC to compile the program
with optimisations and code improvements at the cost of taking longer to compile.
This will often make the program run much faster and perform more aggressive
optimisations such as inlining and specialisation.
For even more optimisations that may take significantly longer to compile,
-O2
is also available.
An interactive environment
GHC provides an interactive environment in a form of a
Read-Evaluate-Print Loop (REPL) called GHCi.
To enter the environment run the program ghci
.
➜ ghci
GHCi, version 9.0.1: https://www.haskell.org/ghc/ :? for help
ghci>
It provides an interactive prompt where Haskell expressions can be written and evaluated.
For example:
ghci> 1 + 1
2
ghci> putStrLn "Hello, world!"
Hello, world!
We can define new names:
ghci> double x = x + x
ghci> double 2
4
We can write multi-line code by surrounding it with :{
and :}
:
ghci> :{
| map f list =
| case list of
| [] -> []
| x : xs -> f x : map f xs
| :}
ghci> map (+1) [1, 2, 3]
[2,3,4]
We can import Haskell source files using the :load
command (:l
for short):
ghci> :load hello.hs
[1 of 1] Compiling Main ( hello.hs, interpreted )
Ok, one module loaded.
ghci> main
Hello, Haskell!
As well as import library modules:
ghci> import Data.Bits
ghci> shiftL 32 1
64
ghci> clearBit 33 0
32
We can even ask what the type of an expression is using the :type
command
(:t
for short):
λ> :type putStrLn
putStrLn :: String -> IO ()
To exit ghci
, use the :quit
command (or :q
for short)
ghci> :quit
Leaving GHCi.
A more thorough introduction to GHCi can be found in the GHC user guide.
Using external packages in ghci
By default, GHCi can only load and use packages that are included with the GHC installation.
However, users of cabal-install and stack can download and load external packages very easily using the following commands:
cabal-install:
cabal repl --build-depends async --build-depends say
Stack:
stack exec --package async --package say -- ghci
And the modules of the relevant packages will be available for import:
GHCi, version 9.0.1: https://www.haskell.org/ghc/ :? for help
ghci> import Control.Concurrent.Async
ghci> import Say
ghci> concurrently_ (sayString "Hello") (sayString "World")
Hello
World
Stack users can also use this feature with runghc
and ghc
by replacing
ghci
in the command above, and cabal-install users can generate an
environment file that will make async
and say
visible for GHC tools
in the current directory using this command:
cabal install --lib async say --package-env .
Many more packages are waiting for you on Hackage.
Additional information
This article covered the most common usage of the core tools GHC offers. If you'd like to learn more about them, the GHC manual contains additional information on how to use the tools, including how to control the runtime system and how to profile Haskell programs.