{-# OPTIONS_GHC -Wno-missing-export-lists #-} {-# LANGUAGE InstanceSigs #-} -- | Notes taken by Ugnė Pacevičiūtė module Lessons.Lesson03 where import Lessons.Lesson02 ( FEType(..) ) -- | We do not have loops in Haskell, because loops require a -- mutable state (e.g. a counter), so we use recursion! -- This function sums all integers in a given list. -- If the list is empty, it returns 0 as the base case. -- Otherwise, it takes the first element (the head) and adds it to the result of summing the rest of the list (the tail). sumOfInts :: [Int] -> Int sumOfInts :: [Int] -> Int sumOfInts [] = Int 0 sumOfInts (Int h:[Int] t) = Int h Int -> Int -> Int forall a. Num a => a -> a -> a + [Int] -> Int sumOfInts [Int] t -- | This function also recursively sums all integers in a given list. -- But it has an advantage: instead of accumulating of intemediate results -- (needed for + function) in stack, you can accumulate these values in a -- dedicated argument, usually called "accumulator". This approach is called -- "tail recursion" and modern compiles are smart enough to identify this -- technic and optimize it (rewrite it with a machine code loop) so no stack -- at all is used. sumOfInts' :: [Int] -> Int sumOfInts' :: [Int] -> Int sumOfInts' [Int] l = [Int] -> Int -> Int sumOfInts'' [Int] l Int 0 where sumOfInts'' :: [Int] -> Int -> Int sumOfInts'' :: [Int] -> Int -> Int sumOfInts'' [] Int acc = Int acc sumOfInts'' (Int h:[Int] t) Int acc = [Int] -> Int -> Int sumOfInts'' [Int] t (Int acc Int -> Int -> Int forall a. Num a => a -> a -> a + Int h) -- | In Haskell, a typeclass works similarly to an interface in Java. -- -- The 'Command' type is an example of an Algebraic Data Type (ADT). -- It represents a set of possible "commands" that a program might handle. -- -- The left-hand side ('Command') defines the type name. -- The right-hand side lists the constructors ('Dump' and 'Sum'). -- -- Each constructor defines a different *form* that a 'Command' value can take: -- 'Dump' wraps a 'Dumpable' value. -- 'Sum' wraps a list of integers that can be processed. data Dumpable = Examples data Command = Dump Dumpable | Sum [Int] -- | This instance tells Haskell how to display values of type 'Dumpable' -- when they are converted to a string using the 'show' function. -- In this example, we have only one constructor, 'Examples', which -- will simply be displayed as the word "examples". -- Normally, Haskell can automatically create a default implementation -- when we write 'deriving Show', but here we define our own for learning purposes. instance Show Dumpable where show :: Dumpable -> String show :: Dumpable -> String show Dumpable Examples = String "examples" -- | Here we define a custom 'Show' instance for the 'Command' type. -- We want each command to be represented as a descriptive string. -- If the command is 'Dump', it will print as "dump " followed by -- whatever the inner 'Dumpable' value looks like. -- If it is 'Sum is', it will print as "sum_of " followed by the list of numbers. instance Show Command where show :: Command -> String show :: Command -> String show (Dump Dumpable d) = String "dump " String -> ShowS forall a. [a] -> [a] -> [a] ++ Dumpable -> String forall a. Show a => a -> String show Dumpable d show (Sum [Int] is) = String "sum_of " String -> ShowS forall a. [a] -> [a] -> [a] ++ [Int] -> String forall a. Show a => a -> String show [Int] is -- | The 'FuzzyAdd' typeclass represents a group of types that can be -- combined using an “fuzzy” addition operation. -- The operator (~+~) defines the behavior for this custom kind of addition. class FuzzyAdd a where (~+~) :: a -> a -> a -- | This instance defines how 'FuzzyAdd' works for integers. -- Instead of performing a normal sum, it adds two numbersa -- and then subtracts one. This is just an illustrative example -- showing how we can redefine arithmetic operators in flexible ways. instance FuzzyAdd Integer where (~+~) :: Integer -> Integer -> Integer ~+~ :: Integer -> Integer -> Integer (~+~) Integer a Integer b = Integer a Integer -> Integer -> Integer forall a. Num a => a -> a -> a + Integer b Integer -> Integer -> Integer forall a. Num a => a -> a -> a - Integer 1 a1 :: Integer a1 :: Integer a1 = Integer 42 -- | FuzzyAdd use case. -- -- >>> a1 ~+~ a2 -- 42 a2 :: Integer a2 :: Integer a2 = Integer 1 -- | Records are usual ADTs which allow to name fields. -- The 'FireExtinguisher' type represents a simple data structure -- describing a fire extinguisher. Each extinguisher has a capacity -- and a type ('FEType'), which is imported from another module. -- Records automatically provide accessors for record's fields. data FireExtinguisher = FireExtinguisher { FireExtinguisher -> Integer capacity :: Integer , FireExtinguisher -> FEType feType :: FEType } deriving Int -> FireExtinguisher -> ShowS [FireExtinguisher] -> ShowS FireExtinguisher -> String (Int -> FireExtinguisher -> ShowS) -> (FireExtinguisher -> String) -> ([FireExtinguisher] -> ShowS) -> Show FireExtinguisher forall a. (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a $cshowsPrec :: Int -> FireExtinguisher -> ShowS showsPrec :: Int -> FireExtinguisher -> ShowS $cshow :: FireExtinguisher -> String show :: FireExtinguisher -> String $cshowList :: [FireExtinguisher] -> ShowS showList :: [FireExtinguisher] -> ShowS Show -- | This value represents a specific example of a fire extinguisher. -- It has a capacity of 10 and is of type 'A'. -- Accessor example: -- -- >>> capacity fe -- 10 fe :: FireExtinguisher fe :: FireExtinguisher fe = Integer -> FEType -> FireExtinguisher FireExtinguisher Integer 10 FEType A -- | This example shows how to use record update syntax in Haskell. -- It creates a new extinguisher based on 'fe', but with the capacity -- changed to 100, while keeping all other fields the same. fe2 :: FireExtinguisher fe2 :: FireExtinguisher fe2 = FireExtinguisher fe { capacity = 100 } -- | This function extracts the 'capacity' field from a given -- 'FireExtinguisher' record. It demonstrates simple pattern matching -- on a record structure (just like usual ADTs) cpcty :: FireExtinguisher -> Integer cpcty :: FireExtinguisher -> Integer cpcty (FireExtinguisher Integer c FEType _) = Integer c -- | The 'Bucket' data type defines another record-style structure. -- It contains a single field, 'bucketCapacity', which stores how -- much the bucket can hold. The 'deriving Show' clause again -- provides a default string representation. data Bucket = Bucket { Bucket -> Integer bucketCapacity :: Integer } deriving Int -> Bucket -> ShowS [Bucket] -> ShowS Bucket -> String (Int -> Bucket -> ShowS) -> (Bucket -> String) -> ([Bucket] -> ShowS) -> Show Bucket forall a. (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a $cshowsPrec :: Int -> Bucket -> ShowS showsPrec :: Int -> Bucket -> ShowS $cshow :: Bucket -> String show :: Bucket -> String $cshowList :: [Bucket] -> ShowS showList :: [Bucket] -> ShowS Show -- | What if we do not have a value to return? Other languages have nulls -- for that. The 'safeHead' function retrieves the first element of a list -- in a safe way. Normally, calling 'head' on an empty list causes -- a runtime error. To prevent that, this function returns a 'Maybe' value: -- 'Just a' if the list has a first element, or 'Nothing' if it is empty. safeHead :: [a] -> Maybe a safeHead :: forall a. [a] -> Maybe a safeHead [] = Maybe a forall a. Maybe a Nothing safeHead (a h:[a] _) = a -> Maybe a forall a. a -> Maybe a Just a h -- | The 'safeHeadDefault' function is a practical extension of 'safeHead'. -- It takes a list and a default value. If the list is empty, it returns -- the default value; otherwise, it returns the first element of the list. -- Internally, it uses pattern matching on the result of 'safeHead' -- to decide which value to return. safeHeadDefault :: [a] -> a -> a safeHeadDefault :: forall a. [a] -> a -> a safeHeadDefault [a] l a d = case [a] -> Maybe a forall a. [a] -> Maybe a safeHead [a] l of Maybe a Nothing -> a d Just a a -> a a