Why Haskell?
Functional Programming Buzz
Functional programming is gaining momentum, especially with libraries like ReactJS. By embracing functional concepts and immutable data structures, JavaScript developers can write cleaner, more readable code. Haskell takes functional programming to the next level, opening new doors for problem-solving and creativity.
Immutable Everything
In Haskell, everything is immutable. A function cannot have side effects, meaning it always produces the same output for the same input. This predictability makes reasoning about your program easier. Imagine React components without unexpected side effects – that’s the power of purity!
Let’s jump into the world of Haskell.
Install ghc
from your Linux distribution’s package manager or from haskell.org/ghc. After installing ghc
, open a terminal emulator and run the command ghci
. This command comes from the ghc
package.
Declaring a variable
Variables can be global or function scoped and we declare them like this: name = <initial_value>
.
myNumber = 1
Don’t put semi colons at the end. In Haskell, semi colons are not for ending a line. Haskell uses indentation instead. Indentation must be at least one space or more.
If the compiler throws error, indent more.
Declare types
Haskell is a full statically typed language but you really don’t need to declare types for simple expected variables or functions, they will auto generate at the compile time or runtime if using GHCI.
To declare a type for a variable, use the following syntax
name :: TYPE
The double colon ::
is the seperator, the left hand side is always the name and the right hand side is the type.
Haskell has several types, some most common types are, Int, String, Char, Double, Bool
.
myName :: String
myName = "Rishav"
From the type we can say that myName
is a variable of String type.
For a function, the type declaration must have one or more argument type and a return type.
Let’s say a function isOdd
to check if a number is odd or not. Then the function’s type be:
isOdd :: Int -> Bool
The ->
is seperates the arguments and the return type. As said you can have several arguments.
someArgs :: Int -> String -> Bool
Here the Int and the String are two seperate arguments, The last thing, the Bool
is the return type.
Writing first function
As said, a function consists of a name, one or more arguments and an expression.
-- Single argument
name arg = expression
-- Multiple arguments
name arg1 arg2 ... = expression
A real example of a function:
isOdd :: Int -> Bool
isOdd num = odd num
We can see the type and say that the function isOdd
takes only one argument of type integer and return a logical value.
Honestly you don’t need to write types for simple functions, Haskell is smart enough to auto generate.
Lists and Tuples
These are the most fundamentals of Haskell.
Lists
In Haskell, lists are fundamental data structures. They can hold elements of the same type. Lists are basically linked lists under the hood. Here’s how you define a list:
myList :: [Int]
myList = [1, 2, 3, 4, 5]
You can also generate a list of numbers by defining a list []
, a start index e.g. 1
, followed by double period ..
and lastly an end value e.g. 5
.
myList' = [Int]
myList' = [1..5]
-- myList' is will now be [1, 2, 3, 4, 5]
Did you notice I just put a single quote at the end of the variable name?
Well in Haskell you are allowed to use a signle quote as a variable name too.
What if I do
myList' = [1..]
?
Well in that case you have just created an infinite loop. If you call myList'
then it won’t stop producing an integer until the process is terminated.
And if you’re wondering, the list generation works for alphabets too!
AtoZ = ['a'..'z']
-- AtoZ will be "abcdefghijklmnopqrstuvwxyz"
Just make sure that the start and the end values are a Char
type.
The generated output is a String
which actually is a list of characters.
You can also add “steps” to generate a list. Steps are given before the ..
seperated by a comma.
-- even numbers from 0 to 10
evenNumbers' = [0, 2 .. 10]
-- this will return [0,2,4,6,8,10]
Solving a basic coding problem
Print all the odd numbers from 1 to an integer N
As from the problem statement, we can use the “steps” to generate the list of odds.
myOdds :: Int -> [Int]
myOdds n = [1, 3 .. n]
-- myOdds 12 -> [1,3,5,7,9,11]
Easy peasy lemon squeezy.
Get the maximum value from an array of integers
Haskell has built-in maximum
, minimum
, compare
, mod
etc function so you don’t have to.
getMax :: [Int] -> Int
getMax arr = maximum arr
-- The same can be written as this because of "currying"
getMax' :: [Int] -> Int
getMax' = maximum
For minimum just put minimum
instead of maximum.
Tuples
Tuples, on the other hand, can hold elements of different types in pair. For example:
myTuple :: (Int, String)
myTuple = (42, "Answer")
Your can put 2 or more values too but generally we don’t go beyond 2.
Tuples has their own function like fst
, snd
and as you can guess, fst
will return the first element and snd
will return the second.
myName = ("Rishav", "Mandal")
getMyFirstName = fst myName
getMyLastName = snd myName
A more practical usage of tuples are storing data for something with it. Like this example:
myName = "Rishav"
myName' = (length myName, myName) -- length function will reutrn the length of a string
-- myName -> (6, "Rishav")
List Comprehension
List comprehensions are powerful tools in Haskell. They allow concise creation of lists based on existing ones. For example:
addOne :: [Int]
addOne = [n+1 | n <- [1..10]]
The syntax is very easy to under stand, the above will be this:
From a list of one to ten, we take each number and add one to it.
The cherry on top that we can also use predicates here.
Predicate: is a function that determines whether something is true or not.
addOne' :: [Int]
addOne' = [n+1 | n <- [1..10], n > 3]
Predicates are written on the right side seperated by a comma.
The above code can be said as:
From a list of one to ten, we take each number such that the number is greater than three and add one to it.
Higher order functions: Map and Filter
I will only disscuss about these two although Haskell has much more.
Map
The map
function applies a given function to each element of a list just similar to JavaScript’s map. For example:
doubleList :: [Int] -> [Int]
doubleList xs = map (* 2) xs
The abive code will multiply each element from the list by 2 and create a new list.
Filter
The filter
function selects elements from a list that satisfy a given condition. For instance:
evenNumbers :: [Int] -> [Int]
evenNumbers xs = filter even xs
The above code will return a new list of even numbers.
Remember, both map and filter returns a new list and not in-place.
Lazy loading
Haskell is a lazy evaluated language, it uses currying technique in all the functions and converts one or more arguments into a sequence. So the above evenNumbers
function can be written as below:
evenNumbers = filter even
This is exact same as the above with the xs
argument.
Lambda Functions
In Haskell, functions are first-class citizens. You can pass them as arguments, return them from other functions, and assign them to variables. Lambda functions allow you to create anonymous functions on the fly:
addOne :: Int -> Int
addOne = \x -> x + 1
Function Composition
Haskell encourages function composition. You can combine functions to create new ones:
squareAndDouble :: Int -> Int
squareAndDouble = (* 2) . (+ 2)
The $
Operator
The $
operator is your friend. It helps avoid parentheses and clarifies function application:
result = sum $ map (* 2) [1 .. 10]
Without the $
we have to write the function as:
result = sum (map (* 2) [1 .. 10])
Type and Record
Type
You can define custom data types using the data
keyword. For example:
data Person = Person
{ name :: String
, age :: Int
}
Record
Records allow you to create structured data with named fields:
alice :: Person
alice = Person {name = "Alice", age = 30}
Some common coding problems are solved in Haskell
-
Find the Last Element of a List:
myLast :: [a] -> a myLast xs = last xs
-
Find the Second-Last Element of a List:
myButLast :: [a] -> a myButLast xs = xs !! (length xs - 2)
-
Find the K’th Element of a List:
elementAt :: [a] -> Int -> a elementAt xs n = xs !! (n - 1)
-
Reverse a List:
myReverse :: [a] -> [a] myReverse xs = reverse xs
-
Check if a List is a Palindrome:
isPalindrome :: Eq a => [a] -> Bool isPalindrome xs = xs == reverse xs
Now you’re equipped with the basics of Haskell. Dive deeper, explore monads, and embrace the elegance of functional programming! 🚀