8.8 KiB
First steps
In this guide we'll take a look at a few core tools that are installed
with the Haskell toolchain, namely, ghc
, runghc
and ghci
.
These tools can be used to compile, interpret or explore Haskell programs.
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 8.10.7
If this fails, consult the Getting started 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 ...
For more in-depth information about the files ghc
produces,
follow the GHC user guide guide.
Now we run our program:
➜ ./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.
Turning on warnings
The -Wall
flag will enable GHC to emit warnings about our code.
➜ 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 ...
While Haskell can infer the types of most expressions, it is recommended that top-level definitions are annotated with their types.
Now our hello.hs
source file should looks like this:
main :: IO ()
main = putStrLn "Hello, world!"
And now GHC will compile hello.hs
without warnings.
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.2: 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 the cabal-install and stack build tools can download and load external packages very easily using the following commands:
cabal-install:
cabal repl --build-depends async,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.
Creating a proper package with modules
The previous methods to compile Haskell code are for quick experiments and small
programs. Usually in Haskell, we create cabal projects, where build tools such as
cabal-install
or stack
will install necessary dependencies and compile modules
in correct order. For simplicity's sake, this section will only use cabal-install
.
To get started, run:
mkdir haskell-project
cd haskell-project
cabal init --interactive
If you let it generate a simple project with sensible defaults, then you should have these files:
src/MyLib.hs
: the library module of your projectapp/Main.hs
: the entry point of your projecthaskell-project.cabal
: the "cabal" file, describing your project, its dependencies and how it's built
To build the project, run:
cabal build
To run the main executable, run:
➜ cabal run
Hello, Haskell!
someFunc
Adding dependencies
Now let's add a dependency and adjust our library module. Open haskell-project.cabal
and find the library section:
library
exposed-modules: MyLib
-- Modules included in this library but not exported.
-- other-modules:
-- LANGUAGE extensions used by modules in this package.
-- other-extensions:
build-depends: base ^>=4.14.3.0
hs-source-dirs: src
default-language: Haskell2010
The interesting parts here are exposed-modules
and build-depends
.
To add a dependency, it should look like this:
build-depends: base ^>=4.14.3.0
, directory
Now open src/MyLib.hs
and change it to:
module MyLib (someFunc) where
import System.Directory
someFunc :: IO ()
someFunc = do
contents <- listDirectory "src"
putStrLn (show contents)
Adding modules
To add a module to your package, adjust exposed-modules
, like so
exposed-modules: MyLib
OtherLib
then create src/OtherLib.hs
with the following contents:
module OtherLib where
otherFunc :: String -> Int
otherFunc str = length str
To use this function interactively, we can run:
➜ cabal repl
ghci> import OtherLib
ghci> otherFunc "Hello Haskell"
13
For further information about how to manage Haskell projects see the Cabal user guide.
Where to go from here
How to learn Haskell proper
To learn Haskell, try any of those:
- A beginner friendly 4-lectures course with exercises (by Kowainik)
- An in-depth university CIS 194 Haskell course including exercises (by Brent Yorgey)