Skip to content

Instantly share code, notes, and snippets.

@rishabhjoshi
Created November 18, 2015 16:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rishabhjoshi/a8009aee874ab728a993 to your computer and use it in GitHub Desktop.
Save rishabhjoshi/a8009aee874ab728a993 to your computer and use it in GitHub Desktop.

Haskell

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.

Hello World

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>"

Comments:

Single line comments: They start with two dashes.

 '-- your comment'

Multiline comments: Enclosed in a block like this -

'{- your comment
comment in next line
-}'

Basics

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!"

Lists

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] 

Tuples

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

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

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

Type Signatures

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

Flow Control

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

Data Types

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`

Input

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment