Haskell is a standardized, general-purpose purely functional programming language, with non-strict semantics and strong static typing. It is named after logician Haskell Curry.
Here I have explained basics of Haskell with complete examples. However, you might have doubts even after reading them because I have not included all details. For an exhaustive reference, read the official documentation.
module Main where
main :: IO ()
main = putStrLn "Hello, World!"
Note that all but the last line can be omitted.
This is a hello world program in Haskell.
To print something, write main = putStrLn "<whatever you want to print>"
Single line comments: They start with two dashes.
'-- your comment'
Multiline comments: Enclosed in a block like this -
'{- your comment
comment in next line
-}'
You have numbers and basic operations
5 -- number 5
5 + 8 -- 13
12 * 3 -- 36
7 / 2 -- 3.5 Division is not integer division by default
7 div
2 -- 3 Integer division
True
False -- Boolean values are primitive
not True -- False
2 /= 2 -- False
4 < 8 -- True
Here not is a function that takes one parameter. Haskell doesn't need parentheses for function calls eg : for a function with name func and 3 arguments you write -
func arg1 arg2 arg3
"This is a string."
String is implemented as a list of characters
['H', 'e', 'y'] -- "Hey"
'a' -- character
Concatenation is done using "++" operator
"Hello " ++ "world!" --"Hello world!"
Every element is of same type. You cant have a list that contains a number and a character.
eg - [1,2,3,4,5] is the same as - [1..5]
1..5 enumerates the numbers from 1 to 5.
1,3..11 enumerates the numbers from 1 to 11 skipping 1 number.
This is same as - 1, 3, 5, 7, 9, 11
For indexing you use "!!" operator.
[1,2,3,4,5] !! 2 -- 3
[0,2..10] -- [0, 2, 4, 6, 8, 10]
[5,4..1] -- [5, 4, 3, 2, 1]
You can have infinite lists in Haskell. Remember Haskell has lazy evaluation though. It will evaluate anything only if it needs to do so.
[1..] !! 25 -- 26
This is completely valid.
The "++" operator can be used to join 2 lists also.
eg - [1..5] ++ [6..10]
You can append to a list by using a ":".
The syntax is - value_to_be_appended:list_name
0:[1..5] -- [0, 1, 2, 3, 4, 5]
Some common functions on a list:
head <list> -- gives 1st element
tail <list> -- gives everything except head
last <list> -- gives last element
init <list> -- gives everything except last
If you wanted to add each element of a list ([1..5]) with 2, you would do it this way. Also with condition that the new numbers should be great than 5.
[x+2 | x <- [1..5]] -- [3, 4, 5, 6, 7]
[x+2 | x <- [1..5], x+2 > 5] -- [6, 7]
Every element can have a different type, but the length of a tuple is fixed.
("hello", 10)
You can access elements of a pair (tuple of length 2) as -
fst <tuple>
snd <tuple>
Gives first and second element respectively.
Variables are dealt in Haskell the same way as python. To assign a variable x value 5, and variable y as "hello", you write :
x <- 5
y <- "hello"
Functions take parameters that are just listed without enclosing parantheses. example of a function that takes 2 parameters
add a b = a + b
Using this
add 1 2 -- 3
-- or
1 `add` 2 -- 3
You can also define a function that has no letters. Essentially your very own operators.
(//) a b = a `div` b
35 // 4 -- 8 Integer division
Guards are an easy way to do branching in functions.
fib x
| x < 2 = 1
| otherwise = fib (x-1) + fib (x-2)
Pattern matching is similar. We can give different definitions for fib. Haskell will automatically call the first function that matches the pattern of the value
fib 1 = 1
fib 2 = 2
fib x = fib (x-1) + fib(x-2)
Patterns can be matched on tuples also.
foo(x, y) = (x+1, y+2)
Pattern matching on lists. Here x
is the first element in the list, and xs
is the rest of the list. We can write our own map function:
myMap func [] = []
myMap func (x:xs) = func x:(myMap func xs)
Anonymous functions can be created with a backslash followed by all the arguments.
myMap (\x -> x + 2) [1..5] -- [3, 4, 5, 6, 7]
Haskell supports partial application. That means if you dont pass in all the arguments that a function requires, then Haskell will return a function that takes the rest of the arguments.
example -
add a b = a + b
foo = add 10 -- foo is now a function that takes a -- number and add 10 to it.
foo 5 -- 15
bar = (+10) -- same as foo
bar 5 -- 15
Multiple functions can be composed together using a dot (.) operator.
Here lol is a function that takes a value and adds 10 to it. It then multiplies the result by 4 and return the final value.
lol = (*4) . (+10)
-- (5 + 10) * 4 = 60
lol 5 -- 60
Haskell has a $
operator that applies a function to a given parameter. In contrast to standard function application, which has highest possible priority of 10 and is left-associative, the $
operator has priority of 0 and is right-associative. Such a low priority means that the expression on its right is applied as the parameter to the function on its left.
example :
-- before
even (fib 7) -- false
-- equivalent
even $ fib 7 -- false
-- composing functions
even . fib $ 7 -- false
Haskell has a very strong type system, and eveything has a type signature.
some basic types :
5 :: Integer
"hello" :: String
True :: Bool
Functions have type too. not
take a boolean and returns a boolean
not :: Bool -> Bool
<func> :: <what it takes> -> <what it gives>
Function that takes two arguments.
add :: Integer -> Integer -> Integer
When you define a value, it's good practice to write its type above it.
double :: Integer -> Integer
double x = x * 2
if expressions are:
yo = if 2<3 then "yes" else "no" --yo = "yes"
can be in multiple lines too, but then you will have to indent.
yo = if 2<3
then "yes"
else "no"
Case expressions. Use this to parse command line arguments.
case args of
"help" -> printHelp
"start" -> startProgram
_ -> putStrLn "Bad args"
Haskell doesnt have loops. Because "To iterate is human, to recurse divine".
map applies a function to every element of an array
map (*2) [1..5] --[2,4,6,8,10]
for function using map:
for array func = map func array
for [0..5] $ \i -> show i
-- or
for [0..5] show
You can even make your own data types in Haskell. Data types can have parameters too.
examples:
data Colour = Red | Blue | Green
data Maybe a = Nothing | Just a
say :: Color -> String
say Red = "You are Red!"
say Blue = "You are blue"
say Green = "You are green"
-- all types of Maybe
Just "hello" -- of the type `Maybe String`
Just 1 -- of the type `Maybe Int`
Nothing -- of the type `Maybe a` for any `a`
When a Haskell program is executed, main
is called. It must return a value of type IO ()
. eg:
main :: IO ()
main = putStrLn $ "Hello World!"
putStrLn has type String -> IO ()
It is easiest to do IO if you can implement your program as a function from String to String.
<function> :: (String -> String) -> IO ()
This inputs some text, runs a function on it, and prints the result.
Examples :
countLines :: String -> String
countLines = show . length . lines
main' = countlines
You can think of a value of type IO ()
as being a sequence of actions to be performed. We can use the do
notation to chain actions together.
main = sayHello
sayHello :: IO ()
sayHello = do
putStrLn "What is your name"
name <- getLine
putStrLn $ "Hello, " ++ name
getLine has type -
getLine :: IO String
IO a
represents a computer program that generates a value of type a
when executed.
We can make our own action of type IO String
as:
action :: IO String
action = do
putStrLn "This is a line"
input1 <- getLine
input2 <- getLine
-- `return` is not a keyword, but a function
-- return :: String -> IO String
return (input1 ++ " " ++ input2)
We can use this "action" just like getLine.
sayHello :: IO ()
sayHello = do
putStrLn "Echo 2 lines"
name <- action
putStrLn result
For more info about Haskell, check out the official documentation.