Lire en français

Haskell Tutorial

Act I

1. Introduction

Programming in Haskell is different from the imperative programming you might be accustomed to. Haskell is a pure functional language, which means it is based on functions and avoids side effects.
Here are some basics of programming in Haskell:
1. Functions: In Haskell, functions are first-class citizens. This means you can pass them as arguments to other functions, return them as values from functions, and store them in data structures.
2. Immutability: Data is immutable by default in Haskell. This means once a value is defined, it cannot be changed. Instead, functions produce new values from input data.
3. Lazy Evaluation: Haskell uses lazy evaluation. This means expressions are only evaluated when their value is needed. This allows writing efficient programs even with potentially infinite data.
4. Strong Static Types: Haskell is strongly typed, meaning each expression has a determined type at compile time. This ensures high program safety and reliability by detecting type errors at compilation rather than at runtime.
5. Pattern Matching: Haskell offers a powerful pattern matching mechanism that allows for concise and expressive decomposition of data structures.
6. Infinite Lists: Thanks to lazy evaluation, Haskell allows manipulation of infinite lists. You can define an infinite list and only evaluate the elements needed.
7. Recursion: Like in most functional programming languages, recursion is a key concept in Haskell. Loops are generally replaced by recursion, enabling writing functions in a declarative manner.
8. Monads: Monads are a powerful abstraction in Haskell for handling side effects such as input/output, error handling, or probabilistic computation. They enable sequencing computations safely and concisely.
In summary, Haskell programming relies on pure functions, strong static types, lazy evaluation, and other functional concepts to create safe, efficient, and expressive programs. While it may seem different at first, Haskell offers an elegant and powerful approach to programming once you get used to it.

2. Function Example

Here's a simple example of a function in Haskell that calculates the sum of elements in a list:
-- Definition of the sumList function which takes a list of integers as input and returns their sum
sumList :: [Int] -> Int          -- Function type: takes a list of integers as input and returns an integer
sumList [] = 0                   -- Base case: if the list is empty, the sum is 0
sumList (x:xs) = x + sumList xs  -- Recursive case: adds the head element of the list to the sum of the remaining list

-- Example of using the function
main = do
    let myList = [1, 2, 3, 4, 5]
    putStrLn $ "The sum of the list " ++ show myList ++ " is " ++ show (sumList myList)

In this example:
- The function `sumList` takes a list of integers `[Int]` as input and returns an integer `Int`.
- It uses pattern matching to define two cases:
- If the list is empty (`[]`), the sum is 0.
- Otherwise, the function adds the first element `x` of the list to the sum of the remaining elements (`xs`), using recursion.
- The usage example in the `main` function defines a list `myList` and prints the sum of its elements using the `sumList` function.
Compile with ghc :
$ ghc sum.hs
[1 of 1] Compiling Main             ( sum.hs, sum.o )
Linking sum ...

$ ./sum
The sum of the list [1,2,3,4,5] is 15

3. Main Data Structures in Haskell

In Haskell, the main data structures include lists, tuples, and algebraic data types. Here's an overview of each of these data structures:
1. Lists: Lists are ordered collections of elements of the same type. They are defined using square brackets and can contain zero or more elements. Lists can be constructed recursively using the cons operator `:` to add an element to an existing list. For example:
 [1, 2, 3, 4
2. Tuples: Tuples are ordered collections of elements of different types. Unlike lists, tuples have a fixed size and cannot be modified. They are defined using parentheses and can contain any number of elements. For example:
 (1, "hello", True
3. Algebraic Data Types: Haskell allows defining custom data types using the `data` declaration. This enables creating composite data types containing multiple fields, similar to structures in imperative languages. For example:
 data Person = Person String Int 
This code creates a new data type `Person` with two fields: a name of type `String` and an age of type `Int`.
In addition to these basic data structures, Haskell also offers more advanced data types such as arrays, sets, and trees, which can be defined using third-party libraries or custom data types.
Here's an example of using the `Person``Person` data type in Haskell:
-- Definition of the Person data type
data Person = Person String Int

-- Function to display a person
showPerson :: Person -> String
showPerson (Person name age) = "Name: " ++ name ++ ", Age: " ++ show age

-- Example of use
main :: IO ()
main = do
    let alice = Person "Alice" 25
        ethan = Person "Ethan" 30
    putStrLn $ "Informations about Alice : " ++ showPerson alice
    putStrLn $ "Informations about Ethan : " ++ showPerson ethan

In this example:
- The `Person` data type is defined with two fields: `name` of type `String` and `age` of type `Int`.
- A `showPerson` function is defined to display the information of a person.
- In the `main` function, two persons, Alice and Ethan, are created using the `Person` constructor.
- Information about each person is displayed using the `showPerson` function.
Executing this program will display information about Alice and Ethan:
Information about Alice: Name: Alice, Age: 25
Information about Ethan: Name: Ethan, Age: 30
This example demonstrates how to define and use a custom data type in Haskell to represent entities with specific attributes.

4. Main Types in Haskell

In Haskell, data types are a fundamental concept. Here are some of the main data types in Haskell:
1. Basic Types:
- `Int`: Fixed-precision signed integers.
- `Integer`: Unlimited-precision signed integers.
- `Float`: Single-precision floating-point numbers.
- `Double`: Double-precision floating-point numbers.
- `Char`: Unicode characters.
- `Bool`: Boolean values `True` or `False`.
2. Composite Types:
- Lists: Ordered collection of elements of the same type.
- Tuples: Ordered collection of elements of different types.
- Algebraic Data Types: Custom data types defined using the `data` declaration.
3. Function Types:
- `->`: Function type. For example, `Int -> Int` represents a function taking an integer input and returning an integer output.
4. Polymorphic Types:
- `a`: Polymorphic variable type. It can represent any type.
- `Maybe a`: Type representing an optional value that can be `Just a` (with a value) or `Nothing` (without a value).
5. Complex Types:
- Lists of lists: For example, `[Int]`` represents a list of integers, `[[Int]]` represents a list of lists of integers, and so on.
- Higher-order Functions: Functions can take other functions as arguments or return them as results.
6. Derived Types:
- New types derived from existing basic or composite types using keywords such as `type` and `newtype`.
These types form the foundation of programming in Haskell and provide great expressiveness for defining data structures and functions with a high degree of safety and precision typical of Haskell's strong static typing.

5. Derived Types with `type` and `newtype`

Here are examples of using the `type` and `newtype` keywords to create derived types in Haskell:
1. Using `type` :
-- Definition of a derived type to represent usernames
type Username = String

-- Using the derived type
showUsername :: Username -> String
showUsername username = "Username: " ++ username

main :: IO ()
main = do
    let user = "JohnDoe"
    putStrLn $ showUsername user
In this example, `Username` is a type alias for `String`. This allows giving additional semantic meaning to a string without introducing distinct new types.
2. Using `newtype` :
-- Definition of a new derived type to represent user ages
newtype Age = Age Int

-- Function to display an age
showAge :: Age -> String
showAge (Age age) = "Age : " ++ show age

main :: IO ()
main = do
    let userAge = Age 30
    putStrLn $ showAge userAge
In this example, `Age` is a new derived type from `Int` using `newtype`. This creates a new type distinct from `Int` while retaining a similar internal representation. This can be useful for avoiding type errors and improving code readability.

6. Difference between `type` and `newtype`

In Haskell, `type` is used to create type aliases, while `newtype` is used to create distinct new types with specific behaviors.
1. Type Synonym (type):
- `type` is used to create type aliases, meaning both types are interchangeable and behave the same way.
- For example, `type UserName = String` creates an alias `UserName` for the type `String`. Both types are essentially identical and can be used interchangeably.
- Type synonyms are primarily used to make the code more readable and to express the developer's intent.
2. Newtype:
- `newtype` is used to create a distinct new type, even if it's based on an existing type.
- It allows defining type instances and behaviors specific to this new type without having to worry about instances of the underlying type.
- For example, `newtype Meter = Meter Double` creates a new type `Meter` that is distinct from `Double`, even though it has the same underlying representation.
- Newtypes offer additional safety as they prevent type errors by ensuring different types won't be accidentally mixed.
In summary, `type` creates a type alias, while `newtype` introduces a distinct new type with specific properties, thus providing more safety and clarity in the code.

7. `Maybe` Type

`Maybe` is a data type in Haskell used to represent values that can be present or absent. It's an elegant way to handle cases where a value may be undefined or null, while avoiding null reference errors.
In simple terms, `Maybe` can be either:
1. `Just a`: representing an existing value of type `a`.
2. `Nothing`: representing the absence of a value.
For example, suppose you have a function that searches for an element in a list and returns that element if found. If the element is not found, the function could return `Nothing` to indicate that no matching value was found.
Here's an example of a function using `Maybe`:
safeDivide :: Double -> Double -> Maybe Double
safeDivide _ 0 = Nothing
safeDivide x y = Just (x / y)

This `safeDivide` function divides two numbers, but it returns `Nothing` if the second number is zero, thus avoiding a division by zero error.
Here's how you can use this function:
main :: IO ()
main = do
    putStrLn "Enter two numbers:"
    num1 <- readLn
    num2 <- readLn
    case safeDivide num1 num2 of
        Just result -> putStrLn $ "Result of division: " ++ show result
        Nothing -> putStrLn "Cannot divide by zero!"

This use of `Maybe` allows for safe and concise handling of potentially missing values, by forcing the developer to explicitly handle cases where a value may be absent. This encourages robust programming and proper handling of null values.

8. `$` Operator

In Haskell, the symbol `$` is called "function application operator" or "low-precedence operator". It is used to avoid writing unnecessary parentheses when applying functions.
Here's how it works:
1. `f $ x` is equivalent to `f x`.
2. It has low precedence, meaning any expression to its right is evaluated before the function application to its left.
3. It can be used to avoid excessive parentheses, especially when applying nested functions.
Here's an example to illustrate its usage:
-- Both of the following expressions are equivalent:

-- Without $
result1 = sum (map (+1) [1, 2, 3])

-- With $
result2 = sum $ map (+1) [1, 2, 3]

In this example, `sum $ map (+1) [1, 2, 3]` is equivalent to `sum (map (+1) [1, 2, 3])`. The `$` operator avoids extra parentheses around `map (+1) [1, 2, 3]`, making the code more readable and avoiding parenthesizing errors.

9. The `foldl` Function, Left Fold Function

The `foldl` function in Haskell is a standard library function that takes a function, an initial element (also known as an accumulator), and a list as arguments, and then iteratively applies the function to each element of the list, using the initial element as the first argument of the function, and then passing it successively at each iteration.
Here's a simple presentation of the `foldl` function:
Here's its signature:
foldl :: (b -> a -> b) -> b -> [a] -> b
It takes a binary function `b -> a -> b` (where `b` is the type of the accumulator and `a` is the type of the list elements), an initial value of the accumulator of type `b`, a list `[a]`, and then applies the binary function from left to right on each element of the list, accumulating, or passing the result into the accumulator, which can also be an intermediate result.
Here's an example of usage:
-- Sum of elements in a list
sumList :: [Int] -> Int
sumList xs = foldl (+) 0 xs

-- Concatenation of all elements in a list of strings
concatList :: [String] -> String
concatList xs = foldl (++) "" xs

In `sumList`, the `foldl (+) 0 xs` function accumulates the sum of the elements of the list `xs` starting from zero as the initial value.
In `concatList`, the `foldl (++) "" xs` function concatenates all the elements of the list `xs` starting from an empty string as the initial value.

10. The Interactive Interpreter

GHCi, short for "Glasgow Haskell Compiler Interactive", is an interactive interpreter provided with the GHC (Glasgow Haskell Compiler).
GHCi can be launched by typing `ghci` at the command line:
$ ghci
GHCi, version 8.8.4, :? for help

Prelude> print "hello"
"hello"

Prelude> :type print
print :: Show a => a -> IO ()

Prelude> 2 + 2
4

Prelude> square x = x * x

Prelude> square 6
36

Prelude> :type square
square :: Num a => a -> a

Leaving GHCi.
You can load Haskell source files into GHCi using the `:load` command. For example, `:load myfile.hs` will load the file `myfile.hs`.
You can ask GHCi to display the type of an expression using the `:type` command. For example, `:type head` will display the type of the `head` function.

11. `Num a` Type Class

`Num a` is a type class in Haskell. In Haskell, type classes are used to define common behaviors shared by multiple types. The `Num` class represents numeric types, that is, types that support arithmetic operations like addition, subtraction, multiplication, etc.
Thus, when you see `Num a`, it means that `a` is a type that is an instance of the `Num` class, meaning it is numeric. This includes types like `Int`, `Integer`, `Float`, `Double`, etc.
For example, if a function has the signature `foo :: Num a => a -> a`, it means that `foo` takes an argument of numeric type `a` and also returns a result of numeric type `a`, where `a` can be any numeric type.

12. Type Class Constraints

In Haskell, `=>` is used to specify type class constraints. When you see a function signature or type with `=>`, it means that the types used in that signature or type must satisfy the specified type class constraints.
For example, if you see a function signature like this:
foo :: Num a => a -> a
It means that `foo` is a function taking an argument of type `a`, where `a` must be an instance of the `Num` type class (a numeric type), and also returning a result of type `a` which must be the same numeric type.
In short, `=>` is used to impose constraints on the types used in function or type signatures. This ensures that the operations specified in the type class constraints are available for the types used.

13. Initializing a List of 12 Random Numbers

To initialize a list of 12 random numbers in Haskell, you can use the `System.Random` module to generate random numbers and the `Control.Monad` module to import the `replicateM` function. Here’s how you can do it:
import System.Random
import Control.Monad (replicateM)

-- Generate a list of 12 random numbers between 0 and 100 inclusive
randomNumbers :: IO [Int]
randomNumbers = replicateM 12 $ randomRIO (0, 100)

In this example:
- `randomRIO` is a function that generates a random number within the specified range.
- `replicateM` is a function that repeats an action a certain number of times and collects the results into a list.
- Le résultat est une liste de 12 nombres aléatoires entre 0 et 100 inclus.
- The result is a list of 12 random numbers between 0 and 100 inclusive.
You can use this function in your program to get a list of random numbers by simply calling `randomNumbers`.
For example:
main :: IO ()
main = do
    numbers <- randomNumbers
    print numbers
This will display a list of 12 random numbers each time you run your Haskell program.
$ ghc r.hs -package random
[1 of 1] Compiling Main             ( r.hs, r.o )
Linking r ...

$ ./r
[37,99,100,64,28,93,67,53,60,25,33,19]

14. Some Basic List Processing Functions

$ ghci
GHCi, version 8.8.4, :? for help

Prelude> li = [1,2,3,4,5,6]

Prelude> take 2 li
[1,2]

Prelude> drop 2 li
[3,4,5,6]

Prelude> reverse li
[6,5,4,3,2,1]

Prelude> map (+ 2) li
[3,4,5,6,7,8]

Prelude> foldl (\acc x -> acc + x) 0 li
21

Prelude> filter (\x -> (x `mod` 2) == 0) li
[2,4,6]

Prelude> 5 `elem` li  -- Testing for the presence of an element in a list
True

Prelude> 12 `elem` li
False

Prelude> length li
6

Prelude> [0, 100] ++ li
[0,100,1,2,3,4,5,6]

Prelude> import Data.List (find)

Prelude Data.List> find (\x -> x == 4) li
Just 4

Prelude Data.List> :type find
find :: Foldable t => (a -> Bool) -> t a -> Maybe a


15. Naming Fields in a `data` Structure

In Haskell, with `data`, you can also name the fields in each data constructor. This allows you to create data types with named fields to improve the readability and maintainability of your code.
Here is an example of a data type declaration with named fields:
data Person = Person { firstName :: String,
                       lastName :: String,
                       age :: Int,
                       email :: String
                     }

In this example, `Person` is a data type with a single data constructor named `Person`. This data constructor has four named fields: `firstName`, `lastName`, `age`, and `email`, each with its own type.
Using named fields allows you to easily access the values associated with each field in an instance of `Person`. For example:
let john = Person { firstName = "John", lastName = "Doe", age = 30, email = "john@example.com" }
You can then access the individual fields like this:
print (firstName john)  -- Prints "John"
print (age john)  -- Prints 30
Using named fields in `data` types is a convenient way to enhance the clarity and maintainability of your Haskell code.

16. Structure Update

Here is how to create a function that returns a new `data` structure with one field having a different value from the original:
$ ghci
GHCi, version 8.8.4, :? for help

Prelude> data Person = Person { firstName :: String, lastName :: String, age :: Int } deriving (Show)

Prelude> let john1 = Person { firstName = "John", lastName = "Doe", age = 30 }

Prelude> newAge person _age = person { age = _age }

Prelude> let john2 = newAge john1 31

Prelude> john2
Person {firstName = "John", lastName = "Doe", age = 31}
`deriving (Show)` allows the type to be displayed.

17. Algebraic Data Types

In Haskell, it is possible to use to define data types that can take different forms.
Here is an example illustrating this usage with the `data` keyword:
data Shape
  = Circle Float
  | Rectangle Float Float
  | Triangle Float Float Float

This definition allows creating values of the `Shape` type, which can be a `Circle` with a single `Float` argument, a `Rectangle` with two `Float` arguments, or a `Triangle` with three `Float` arguments.
Using Values
To create and use values of this type in Haskell:
let myCircle = Circle 5.0

area :: Shape -> Float
area shape = case shape of
  Circle r -> 3.14 * r * r
  Rectangle w h -> w * h
  Triangle a b c ->    -- area of a triangle
    let s = (a + b + c) / 2
    in sqrt (s * (s - a) * (s - b) * (s - c))

myTriangle :: Shape
myTriangle = Triangle 3.0 4.0 5.0

let triangleArea = area myTriangle

(In the `Shape` type definition for a triangle, the three values represent the lengths of the three sides of the triangle.)
Algebraic Data Types allow you to define data types that can have multiple constructors with different arguments or no arguments at all.
data Dir = Left | Right

18. Extracting a sub-string

$ ghci
GHCi, version 8.8.4, :? for help

Prelude> let subStr s i j = take j (drop i s)

Prelude> let s = "Hello World"

Prelude> subStr s 0 5
"Hello"

Prelude> subStr s 3 5
"lo Wo"

Prelude> :t subStr
subStr :: [a] -> Int -> Int -> [a]

Prelude> :t s
s :: [Char]

19. Tail Recursion

Tail recursion is important in Haskell because it helps prevent stack overflow when executing recursive functions on large inputs. In Haskell, tail recursion is often automatically optimized by the compiler through tail call optimization. However, this optimization only occurs for functions that are written in a particular form known as "tail recursion." To benefit from this optimization, you must ensure that the recursive call is the last operation performed in the function and that there are no additional operations to perform after the recursive call.
Example
-- not tail-rec
sumList1 li =
    case li of
        [] -> 0
        x:xs -> x + sumList1 xs

-- tail-rec
sumList2 li =
    let aux [] sum = sum
        aux (x:xs) sum = aux xs (x + sum)
    in aux li 0

main = do
    let li = [1,2,3,4,5,6]
    print $ sumList1 li
    print $ sumList2 li
In the `sumList1` function, the recursive call `sumList1 xs` must be completed before the addition with `x` can occur. Therefore, the recursive call is not in the tail position.
In contrast, in the `sumList2` function, there are no operations left to perform after the recursive call. The addition is performed by passing an additional parameter, which is returned at the end of all recursive calls.

20. Haskell Indentation

In Haskell, indentation is crucial for defining the structure of the code. Incorrect indentation can lead to syntax errors. Code blocks are not grouped by braces or keywords like in other languages. Instead, lines of code that belong to the same block must be indented at the same level (similar to Python). This enhances readability, ensures syntax consistency, and helps clarify the program's structure.

21. Indexing Operator

In Haskell, the `!!` operator is used to access an element of a list by its index. This operator takes a list and an index as arguments and returns the element located at that index. Indexing starts at 0, meaning that index 0 corresponds to the first element of the list.
Here's an example of using `!!`:
ghci> let list = [10, 20, 30, 40, 50]
ghci> list !! 0
10
ghci> list !! 2
30
ghci> list !! 4
50
In this example, `list !! 0` returns `10`, which is the first element of the list. `list !! 2` returns `30`, which is the third element of the list, and so on.
Technical Details:
- Signature: The `!!` operator has the signature `(!!) :: [a] -> Int -> a`. This means it takes a list of type `[a]` and an `Int` integer, and returns an element of type `a`.
- Safety: If the index is out of bounds of the list, it triggers a runtime error. For example:
ghci> list !! 5
*** Exception: Prelude.!!: index too large
- Performance: Accessing an element via `!!` has a time complexity of (O(n)) where (n) is the position of the element, as it needs to traverse the list up to the specified index. This can be inefficient for long lists.
Example Code
Here's an example Haskell program using `!!`:
main :: IO ()
main = do
  let numbers = [1, 2, 3, 4, 5]
  putStrLn $ "The first element is: " ++ show (numbers !! 0)
  putStrLn $ "The third element is: " ++ show (numbers !! 2)
  putStrLn $ "The fifth element is: " ++ show (numbers !! 4)

This program will output:
The first element is: 1
The third element is: 3
The fifth element is: 5
Alternatives
For more performant data structures where fast access to an element by index is needed, other data structures like vectors (`Vector`) can be considered, which offer (O(1)) access:
import qualified Data.Vector as V

main :: IO ()
main = do
  let numbers = V.fromList [1, 2, 3, 4, 5]
  putStrLn $ "The first element is: " ++ show (numbers V.! 0)
  putStrLn $ "The third element is: " ++ show (numbers V.! 2)
  putStrLn $ "The fifth element is: " ++ show (numbers V.! 4)

By using `Data.Vector`, access to elements by index is much more efficient.

22. The `IO ()` Type

In Haskell, the type `IO ()` is used to represent input/output actions that do not produce a meaningful value.
1. `IO`: It's a type that encapsulates input/output actions. A value of type `IO a` represents an action that, when executed, can interact with the outside world (like reading a file, printing to the screen, etc.) and produces a value of type `a`.
2. `()`, the unit type: It represents a single value, also known as unit, often used to indicate that there's no meaningful value produced. It's somewhat like `void` in other programming languages.
When you combine the two to get `IO ()`, it means we have an input/output action that, when executed, doesn't produce a meaningful value (or, more precisely, produces the value `()`, which is trivial and uninteresting in itself).
Examples
Here's a simple example:
main :: IO ()
main = putStrLn "Hello, World!"

In this code:
- `putStrLn "Hello, World!"` is an input/output action that prints a string to the screen.
- `main` has the type `IO ()`, which means it's an input/output action that, once executed, doesn't produce a meaningful value (the return value is `()`).
Why `IO ()`?
The type `IO ()` is useful for actions where the effect of the action is important (such as writing to a file or displaying a message), but the value produced by this action is not used. In other words, `IO ()` means that we are interested in the side effects of the action, not in a return value.
Managing Side Effects
`IO` actions are essential in Haskell for managing side effects while keeping the language pure. Pure functions have no side effects, which allows for mathematical reasoning about programs. By isolating side effects in `IO` actions, Haskell maintains this purity.
Example with Combined Effects
Here is another example that shows a combination of several `IO ()` actions:
main :: IO ()
main = do
    putStrLn "What is your name?"
    name <- getLine
    putStrLn ("Hello, " ++ name ++ "!")

In this code:
- `putStrLn` and `getLine` are input/output actions.
- The sequence of these actions is combined using `do` notation.
- The type of `main` is still `IO ()` because the final value produced is `()`.
In summary, `IO ()` in Haskell is a type used to represent input/output actions that do not have a significant return value but perform important operations by interacting with the outside world.

23. Exceptions

In Haskell, exceptions are used to handle exceptional situations or errors in a program. Here's how they work:
1. Raising an Exception: To signal an error or an exceptional situation, you can raise an exception using the `throw` function from the `Control.Exception` module. For example:
-- Fonction de division
divide :: Int -> Int -> Int
divide _ 0 = throw DivideByZero
divide x y = x `div` y

Here, if the second argument of the `divide` function is 0, a `DivideByZero` exception is raised.
2. Catching an Exception: To catch and handle an exception, you can use the `catch` function also provided by `Control.Exception`. It takes two arguments: an action to execute and an exception-handling function.
import Control.Exception (ArithException(..), tryJust, evaluate, throw)
import Control.Monad (guard)

-- Put the divide function here.

main :: IO ()
main = do
    -- Attempting to divide by zero.
    result <- tryJust (\e -> guard (e == DivideByZero) >> return e) (evaluate (divide 10 0))
    case result of
        Left DivideByZero -> putStrLn "Division par zéro !"
        Right value -> putStrLn $ "Result: " ++ show value

Here, `tryJust` allows you to specifically catch the `DivideByZero` exception, and the `evaluate` function lets you evaluate the expression, raising an exception if necessary.
3. Types of Exceptions: In Haskell, exceptions are typed. This means that each type of exception is associated with a specific type. For example, `DivideByZero` is a type of exception that represents a division by zero.
4. Safe Exception Handling: Exceptions in Haskell are handled in a pure and safe manner. This means that exceptions cannot be used for unsafe operations as in other programming languages. Exceptions in Haskell are primarily used for error handling within the context of safe functional programming.
Another example with `catch`:
import Control.Exception (ArithException(..), catch, evaluate)

-- Division function
divide :: Int -> Int -> Int
divide x y = x `div` y

main :: IO ()
main = do
    putStrLn "Attempting to divide by zero."
    -- Evaluating the division and handling the exception
    (evaluate (divide 10 0) >>= print) `catch` handler

-- Exception handler
handler :: ArithException -> IO ()
handler DivideByZero = putStrLn "Caught exception: Division by zero"
handler exn = putStrLn $ "Caught another arithmetic exception: " ++ show exn

In summary, exceptions in Haskell provide a safe and efficient mechanism for handling errors and exceptional situations in Haskell programs, clearly distinguishing them from the normal flow of control.

24. Installing Haskell Libraries

To install a Haskell library, you can use your distribution's package manager or the Haskell package manager called `cabal`. Here are the steps to follow:
$ sudo apt-get update

$ sudo apt-cache search ghc
ghc - The Glasgow Haskell Compilation system

$ sudo apt-get install ghc

$ sudo apt-cache search cabal
cabal-install - command-line interface for Cabal and Hackage

$ sudo apt-get install cabal-install

$ cabal update
Downloading the latest package list from hackage.haskell.org

$ cabal install --lib gloss
Hackage is the repository of Haskell libraries.

Act II — Scene 1

25. Hello World with SDL2

Here is a minimal example using the SDL2 graphics library, which opens a window and displays a rectangle:
{-# LANGUAGE OverloadedStrings #-}
import SDL.Vect (V2(..), V4(..), Point(P))
import SDL (($=))
import qualified SDL
import Control.Monad (unless)

main :: IO ()
main = do
    SDL.initialize [SDL.InitVideo]
    window <- SDL.createWindow "Hello World" SDL.defaultWindow { SDL.windowInitialSize = V2 320 240 }
    SDL.showWindow window
    renderer <- SDL.createRenderer window (-1) SDL.defaultRenderer
    loop renderer
    SDL.destroyRenderer renderer
    SDL.destroyWindow window
    SDL.quit

loop :: SDL.Renderer -> IO ()
loop renderer = do
    SDL.rendererDrawColor renderer $= V4 0 0 255 255
    SDL.clear renderer
    SDL.rendererDrawColor renderer $= V4 0 0 0 255
    let squareRect = SDL.Rectangle (P (V2 80 60)) (V2 60 40)  -- pos:(80, 60), size:(60x40)
    --SDL.drawRect renderer (Just squareRect)
    SDL.fillRect renderer (Just squareRect)
    SDL.present renderer
    event_loop renderer

event_loop :: SDL.Renderer -> IO ()
event_loop renderer = do
    maybeEvent <- SDL.pollEvent
    case maybeEvent of
        Just event -> unless (SDL.eventPayload event == SDL.QuitEvent) (event_loop renderer)
        Nothing -> loop renderer
Installing SDL2 for Haskell:
$ sudo apt-cache search sdl2
libghc-sdl2-dev - high- and low-level bindings to the SDL 2 library

$ sudo apt-get install libghc-sdl2-dev
Compiling the example:
$ ghc hello.hs -package sdl2

These examples cover the basics of Haskell; you can now experiment and try to go further!
You can find various examples (with equivalents in other languages) on the site RosettaCode.org.
A good reference to refer to is the Haskell wikibook.
Learning Haskell will do you a lot of good!
Example of games with Haskell: wiki.haskell.org::Games.
Learn X in Y minutes, where X=Haskell

Created with ChatGPT