diff --git a/src/Chapter1.hs b/src/Chapter1.hs index 406deeaca..2f8ed078d 100644 --- a/src/Chapter1.hs +++ b/src/Chapter1.hs @@ -209,31 +209,31 @@ So, the output in this example means that 'False' has type 'Bool'. > Try to guess first and then compare your expectations with GHCi output >>> :t True - +True :: Bool >>> :t 'a' - +'a' :: Char >>> :t 42 - +42 :: Num a => a A pair of boolean and char: >>> :t (True, 'x') - +(True, 'x') :: (Bool, Char) Boolean negation: >>> :t not - +not :: Bool -> Bool Boolean 'and' operator: >>> :t (&&) - +(&&) :: Bool -> Bool -> Bool Addition of two numbers: >>> :t (+) - +(+) :: Num a => a -> a -> a Maximum of two values: >>> :t max - +max :: Ord a => a -> a -> a You might not understand each type at this moment, but don't worry! You've only started your Haskell journey. Types will become your friends soon. @@ -301,43 +301,43 @@ expressions in GHCi functions and operators first. Remember this from the previous task? ;) >>> 1 + 2 - +3 >>> 10 - 15 - +5 >>> 10 - (-5) -- negative constants require () - +15 >>> (3 + 5) < 10 - +True >>> True && False - +False >>> 10 < 20 || 20 < 5 - +True >>> 2 ^ 10 -- power - +1024 >>> not False - +True >>> div 20 3 -- integral division - +6 >>> mod 20 3 -- integral division remainder - +2 >>> max 4 10 - +10 >>> min 5 (max 1 2) - +2 >>> max (min 1 10) (min 5 7) - +5 Because Haskell is a __statically-typed__ language, you see an error each time you try to mix values of different types in situations where you are not @@ -429,9 +429,13 @@ task is to specify the type of this function. 49 -} +squareSum :: Num a => a -> a -> a squareSum x y = (x + y) * (x + y) + + + {- | =⚔️= Task 4 @@ -448,8 +452,8 @@ Implement the function that takes an integer value and returns the next 'Int'. every type 。.☆.*。. No need to worry much about "error" here, just replace the function body with the proper implementation. -} -next :: Int -> Int -next x = error "next: not implemented!" +next :: Num a => a -> a +next x = x + 1 {- | After you've implemented the function (or even during the implementation), you @@ -490,7 +494,8 @@ Implement a function that returns the last digit of a given number. whether it works for you! -} -- DON'T FORGET TO SPECIFY THE TYPE IN HERE -lastDigit n = error "lastDigit: Not implemented!" +lastDigit :: Integral a => a -> a +lastDigit n = n `mod` 10 {- | @@ -519,8 +524,8 @@ branches because it is an expression and it must always return some value. 👩‍🔬 Due to lazy evaluation in Haskell, only the expression from the branch satisfying the check will be returned and, therefore, evaluated. -} -closestToZero :: Int -> Int -> Int -closestToZero x y = error "closestToZero: not implemented!" +closestToZero :: (Ord a, Num a) => a -> a -> a +closestToZero x y = if abs x < abs y then x else y {- | @@ -554,7 +559,21 @@ value after "=" where the condition is true. Casual reminder about adding top-level type signatures for all functions :) -} -mid x y z = error "mid: not implemented!" +mid :: Ord a => a -> a -> a -> a +mid x y z + | (x <= y && y <= z) || (z <= y && y <= x) = y + | (y <= x && x <= z) || (z <= x && x <= y) = x + | otherwise = z + +-- mid x y z = x + y + z - minimum [x, y, z] - maximum [x, y, z] + + +-- mid x y z +-- | x > y = mid y x z +-- | y > z = mid x z y +-- | otherwise = y + + {- | =⚔️= Task 8 @@ -568,7 +587,9 @@ True >>> isVowel 'x' False -} -isVowel c = error "isVowel: not implemented!" +isVowel :: Char -> Bool +isVowel c = c `elem` "aeiouy" + {- | @@ -632,7 +653,16 @@ Try to introduce variables in this task (either with let-in or where) to avoid specifying complex expressions. -} -sumLast2 n = error "sumLast2: Not implemented!" +sumLast2 :: Integral a => a -> a +sumLast2 n = + let lastDigit = n `mod` 10 + secondLastDigit = (n `div` 10) `mod` 10 + in lastDigit + secondLastDigit + + + + + {- | @@ -653,7 +683,10 @@ You need to use recursion in this task. Feel free to return to it later, if you aren't ready for this boss yet! -} -firstDigit n = error "firstDigit: Not implemented!" +firstDigit :: Integral t => t -> t +firstDigit n + | n < 10 = n + | otherwise = firstDigit (n `div` 10) {- @@ -668,3 +701,4 @@ Modules: http://learnyouahaskell.com/modules Let vs where: https://wiki.haskell.org/Let_vs._Where Packages and modules in Haskell: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/packages.html -} + diff --git a/src/Chapter2.hs b/src/Chapter2.hs index b98ceaf7d..6d4617e25 100644 --- a/src/Chapter2.hs +++ b/src/Chapter2.hs @@ -136,42 +136,50 @@ functions in GHCi and insert the corresponding resulting output below: List of booleans: >>> :t [True, False] - +[True, False] :: [Bool] String is a list of characters: >>> :t "some string" +"some string" :: String Empty list: >>> :t [] +[] :: [a] Append two lists: >>> :t (++) +(++) :: [a] -> [a] -> [a] Prepend an element at the beginning of a list: >>> :t (:) +(:) :: a -> [a] -> [a] Reverse a list: >>> :t reverse - +reverse :: [a] -> [a] Take first N elements of a list: >>> :t take +take :: Int -> [a] -> [a] Create a list from N same elements: >>> :t replicate +replicate :: Int -> a -> [a] Split a string by line breaks: >>> :t lines +lines :: String -> [String] Join a list of strings with line breaks: >>> :t unlines +unlines :: [String] -> String -} @@ -186,30 +194,43 @@ Evaluate the following expressions in GHCi and insert the answers. Try to guess first, what you will see. >>> [10, 2] ++ [3, 1, 5] +[10,2,3,1,5] >>> [] ++ [1, 4] -- [] is an empty list +[1,4] >>> 3 : [1, 2] +[3,1,2] >>> 4 : 2 : [5, 10] -- prepend multiple elements +[4,2,5,10] >>> [1 .. 10] -- list ranges +[1,2,3,4,5,6,7,8,9,10] >>> [10 .. 1] +[] >>> [10, 9 .. 1] -- backwards list with explicit step +[10,9,8,7,6,5,4,3,2,1] >>> length [4, 10, 5] -- list length +3 >>> replicate 5 True +[True,True,True,True,True] >>> take 5 "Hello, World!" +"Hello" >>> drop 5 "Hello, World!" +", World!" >>> zip "abc" [1, 2, 3] -- convert two lists to a single list of pairs +[('a',1),('b',2),('c',3)] >>> words "Hello Haskell World!" -- split the string into the list of words +["Hello","Haskell","World!"] @@ -336,7 +357,9 @@ from it! ghci> :l src/Chapter2.hs -} subList :: Int -> Int -> [a] -> [a] -subList = error "subList: Not implemented!" +subList start end xs + | start < 0 || end < 0 || start > end = [] + | otherwise = take (end - start + 1) (drop start xs) {- | =⚔️= Task 4 @@ -349,7 +372,9 @@ Implement a function that returns only the first half of a given list. "b" -} -- PUT THE FUNCTION TYPE IN HERE -firstHalf l = error "firstHalf: Not implemented!" + +firstHalf :: [a] -> [a] +firstHalf l = take (length l `div` 2) l {- | @@ -501,7 +526,9 @@ True >>> isThird42 [42, 42, 0, 42] False -} -isThird42 = error "isThird42: Not implemented!" +isThird42 :: [Int] -> Bool +isThird42 (_ : _ : 42 : _) = True +isThird42 _ = False {- | @@ -606,7 +633,11 @@ Implement a function that duplicates each element of the list -} duplicate :: [a] -> [a] -duplicate = error "duplicate: Not implemented!" +duplicate l = reverse (go [] l) + where + go :: [a] -> [a] -> [a] + go acc [] = acc + go acc (x:xs) = go (x : x : acc) xs {- | @@ -621,7 +652,11 @@ Write a function that takes elements of a list only in even positions. >>> takeEven [2, 1, 3, 5, 4] [2,3,4] -} -takeEven = error "takeEven: Not implemented!" + +takeEven :: [a] -> [a] +takeEven [] = [] +takeEven (x:[]) = [x] +takeEven (x : _ : xs) = x : takeEven xs {- | =🛡= Higher-order functions @@ -728,7 +763,7 @@ value of the element itself 🕯 HINT: Use combination of 'map' and 'replicate' -} smartReplicate :: [Int] -> [Int] -smartReplicate l = error "smartReplicate: Not implemented!" +smartReplicate l = concatMap (\x-> replicate x x ) l {- | =⚔️= Task 9 @@ -741,7 +776,9 @@ the list with only those lists that contain a passed element. 🕯 HINT: Use the 'elem' function to check whether an element belongs to a list -} -contains = error "contains: Not implemented!" + +contains :: Eq a => a -> [[a]] -> [[a]] +contains n = filter (elem n) {- | @@ -781,13 +818,15 @@ Let's now try to eta-reduce some of the functions and ensure that we mastered the skill of eta-reducing. -} divideTenBy :: Int -> Int -divideTenBy x = div 10 x +divideTenBy = div 10 -- TODO: type ;) -listElementsLessThan x l = filter (< x) l +listElementsLessThan :: Ord a => a -> [a] -> [a] +listElementsLessThan x = filter (< x) -- Can you eta-reduce this one??? -pairMul xs ys = zipWith (*) xs ys +pairMul :: [Integer] -> [Integer] -> [Integer] +pairMul = zipWith (*) {- | =🛡= Lazy evaluation @@ -842,7 +881,10 @@ list. 🕯 HINT: Use the 'cycle' function -} -rotate = error "rotate: Not implemented!" + +rotate :: Int -> [a] -> [a] +rotate _ [] = [] +rotate n xs = take (length xs) (drop n (cycle xs)) {- | =💣= Task 12* @@ -858,10 +900,19 @@ and reverses it. function, but in this task, you need to implement it manually. No cheating! -} -rewind = error "rewind: Not Implemented!" + +reverseList :: [a] -> [a] +reverseList = go [] + where + go acc [] = acc + go acc (x:xs) = go (x:acc) xs + + + {- You did it! Now it is time to open pull request with your changes and summon @vrom911 for the review! -} + diff --git a/src/Chapter3.hs b/src/Chapter3.hs index 061811064..4b020f544 100644 --- a/src/Chapter3.hs +++ b/src/Chapter3.hs @@ -344,6 +344,13 @@ of a book, but you are not limited only by the book properties we described. Create your own book type of your dreams! -} +data Book = Book + { bookTitle :: String + , bookAuthor :: String + , bookPageCount :: Int + } deriving (Show) + + {- | =⚔️= Task 2 @@ -455,6 +462,48 @@ allow you to model your domain precisely, make illegal states unrepresentable and provide more flexibility when working with data types. -} +data Knight = Knight + { knightHealth :: Int + , knightAttack :: Int + , knightGold :: Int + } deriving (Show) + +data Monster = Monster + { monsterHealth :: Int + , monsterAttack :: Int + , monsterGold :: Int + } deriving (Show) + + +fight :: Monster -> Knight -> Int +fight monster knight = + let monsterHealthAfterHit = monsterHealth monster - knightAttack knight + in if monsterHealthAfterHit <= 0 + then + knightGold knight + monsterGold monster + else + let knightHealthAfterHit = knightHealth knight - monsterAttack monster + in if knightHealthAfterHit <= 0 + then + -1 + else + knightGold knight + +testFight :: IO () +testFight = do + let knight1 = Knight { knightHealth = 100, knightAttack = 30, knightGold = 50 } + let monster1 = Monster { monsterHealth = 20, monsterAttack = 10, monsterGold = 100 } + print $ fight monster1 knight1 -- Expected output: 150 (knight wins and takes loot) + + let knight2 = Knight { knightHealth = 100, knightAttack = 30, knightGold = 50 } + let monster2 = Monster { monsterHealth = 50, monsterAttack = 120, monsterGold = 100 } + print $ fight monster2 knight2 -- Expected output: -1 (monster defeats the knight) + + let knight3 = Knight { knightHealth = 100, knightAttack = 30, knightGold = 50 } + let monster3 = Monster { monsterHealth = 50, monsterAttack = 20, monsterGold = 100 } + print $ fight monster3 knight3 -- Expected output: 50 (no one wins, knight keeps his gold) + + {- | =⚔️= Task 3 @@ -462,6 +511,14 @@ Create a simple enumeration for the meal types (e.g. breakfast). The one who comes up with the most number of names wins the challenge. Use your creativity! -} +data MealType = + Breakfast + | Brunch + | Lunch + | Dinner + | Snack + | Dessert + {- | =⚔️= Task 4 @@ -482,6 +539,89 @@ After defining the city, implement the following functions: and at least 10 living __people__ inside in all houses of the city in total. -} +data Occupancy = + OnePerson + | TwoPeople + | ThreePeople + | FourPeople + deriving (Show, Eq) + +data House = House + { houseOccupancy :: Occupancy + } deriving (Show, Eq) + +data Building = Church | Library + deriving (Show, Eq) +data Castle = Castle + { castleName :: String + , castleHasWall :: Bool + } deriving (Show, Eq) + +data City = City + { cityCastle :: Maybe Castle + , cityBuilding :: Building + , cityHouses :: [House] + } deriving (Show, Eq) + +buildCastle :: String -> City -> City +buildCastle name city = city { cityCastle = Just (Castle name False) } + + +buildHouse :: Occupancy -> City -> City +buildHouse occupancy city = city { cityHouses = House occupancy : cityHouses city } + +countPeople :: House -> Int +countPeople house = case houseOccupancy house of + OnePerson -> 1 + TwoPeople -> 2 + ThreePeople -> 3 + FourPeople -> 4 + +buildWalls :: City -> City +buildWalls city = + case cityCastle city of + Nothing -> city + Just castle -> + let allHouses = cityHouses city + peopleCounts = map countPeople allHouses + totalPeople = sum peopleCounts + in if totalPeople >= 10 + then city {cityCastle = Just (castle {castleHasWall = True})} + else city + + +testBuildWalls :: IO () +testBuildWalls = do + putStrLn "--- Testing buildWalls function ---" + + -- Base cities for our tests + let cityWithCastle = City { cityCastle = Just (Castle "Camelot" False), cityBuilding = Church, cityHouses = [] } + let cityWithoutCastle = City { cityCastle = Nothing, cityBuilding = Library, cityHouses = [House FourPeople, House FourPeople, House FourPeople] } + + -- Test 1: No castle. Walls should not be built, even with enough people. + putStrLn "\n-- Test 1: No Castle (Population: 12) --" + putStrLn $ "Before: " ++ show cityWithoutCastle + putStrLn $ "After: " ++ show (buildWalls cityWithoutCastle) + + -- Test 2: Castle exists, but not enough people (9). Walls should not be built. + let cityWith9People = buildHouse FourPeople (buildHouse FourPeople (buildHouse OnePerson cityWithCastle)) + putStrLn "\n-- Test 2: With Castle (Population: 9) --" + putStrLn $ "Before: " ++ show cityWith9People + putStrLn $ "After: " ++ show (buildWalls cityWith9People) + + -- Test 3: Castle exists, and exactly enough people (10). Walls SHOULD be built. + let cityWith10People = buildHouse FourPeople (buildHouse FourPeople (buildHouse TwoPeople cityWithCastle)) + putStrLn "\n-- Test 3: With Castle (Population: 10) --" + putStrLn $ "Before: " ++ show cityWith10People + putStrLn $ "After: " ++ show (buildWalls cityWith10People) + + -- Test 4: Castle exists, and more than enough people (12). Walls SHOULD be built. + let cityWith12People = buildHouse FourPeople (buildHouse FourPeople (buildHouse FourPeople cityWithCastle)) + putStrLn "\n-- Test 4: With Castle (Population: 12) --" + putStrLn $ "Before: " ++ show cityWith12People + putStrLn $ "After: " ++ show (buildWalls cityWith12People) + + {- =🛡= Newtypes @@ -562,22 +702,30 @@ introducing extra newtypes. 🕯 HINT: if you complete this task properly, you don't need to change the implementation of the "hitPlayer" function at all! -} + +newtype Health = Health Int +newtype Armor = Armor Int +newtype Attack = Attack Int +newtype Dexterity = Dexterity Int +newtype Strength = Strength Int + data Player = Player - { playerHealth :: Int - , playerArmor :: Int - , playerAttack :: Int - , playerDexterity :: Int - , playerStrength :: Int + { playerHealth :: Health + , playerArmor :: Armor + , playerAttack :: Attack + , playerDexterity :: Dexterity + , playerStrength :: Strength } -calculatePlayerDamage :: Int -> Int -> Int -calculatePlayerDamage attack strength = attack + strength +calculatePlayerDamage :: Attack -> Strength -> Int +calculatePlayerDamage (Attack attack) (Strength strength) = attack + strength + +calculatePlayerDefense :: Armor -> Dexterity -> Int +calculatePlayerDefense (Armor armor) (Dexterity dexterity) = armor * dexterity -calculatePlayerDefense :: Int -> Int -> Int -calculatePlayerDefense armor dexterity = armor * dexterity -calculatePlayerHit :: Int -> Int -> Int -> Int -calculatePlayerHit damage defense health = health + defense - damage +calculatePlayerHit :: Int -> Int -> Health -> Health +calculatePlayerHit damage defense (Health health) = Health (health + defense - damage) -- The second player hits first player and the new first player is returned hitPlayer :: Player -> Player -> Player @@ -594,6 +742,9 @@ hitPlayer player1 player2 = (playerHealth player1) in player1 { playerHealth = newHealth } + + + {- | =🛡= Polymorphic data types @@ -755,6 +906,23 @@ parametrise data types in places where values can be of any general type. maybe-treasure ;) -} +data TreasureChest x = TreasureChest + { treasureChestGold :: Int + , treasureChestLoot :: x + } deriving (Show) + +data DragonLair x = DragonLair + { dragon :: Dragon x + , treasureChest :: Maybe (TreasureChest x) + } deriving (Show) + +data Dragon x = Dragon + { dragonName :: String + , dragonPower :: x + } deriving (Show) + + + {- =🛡= Typeclasses @@ -892,6 +1060,7 @@ type exists. You can see how we reuse the fact that the underlying type has this instance and apply this typeclass method to it. -} + {- | =⚔️= Task 7 @@ -913,6 +1082,16 @@ class Append a where append :: a -> a -> a +newtype Gold = Gold Int deriving (Show, Eq) + +instance Append Gold where + append :: Gold -> Gold -> Gold + append (Gold x) (Gold y) = Gold (x + y) + + +data List a = Empty | Cons a (List a) deriving (Show, Eq) + + {- =🛡= Standard Typeclasses and Deriving @@ -973,6 +1152,86 @@ implement the following functions: 🕯 HINT: to implement this task, derive some standard typeclasses -} + +data DayOfWeek + = Monday + | Tuesday + | Wednesday + | Thursday + | Friday + | Saturday + | Sunday + deriving (Show, Eq, Ord) + + + +instance Enum DayOfWeek where + toEnum :: Int -> DayOfWeek + toEnum 0 = Saturday + toEnum 1 = Sunday + toEnum 2 = Monday + toEnum 3 = Tuesday + toEnum 4 = Wednesday + toEnum 5 = Thursday + toEnum _ = Friday + + fromEnum :: DayOfWeek -> Int + fromEnum Saturday = 0 + fromEnum Sunday = 1 + fromEnum Monday = 2 + fromEnum Tuesday = 3 + fromEnum Wednesday = 4 + fromEnum Thursday = 5 + fromEnum Friday = 6 + + +isWeekend :: DayOfWeek -> Bool +isWeekend day = day == Saturday || day == Sunday + + +nextDay :: DayOfWeek -> DayOfWeek +nextDay day = toEnum $ (fromEnum day + 1) `mod` 7 + +daysToParty :: DayOfWeek -> Int +daysToParty day = (6 - fromEnum day + 7) `mod` 7 + + +testDays :: IO () +testDays = do + let days = [Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday] + putStrLn "--- Testing DayOfWeek functions ---" + mapM_ printDayInfo days + +printDayInfo :: DayOfWeek -> IO () +printDayInfo day = do + putStrLn $ "Day: " ++ show day + putStrLn $ " Is Weekend? " ++ show (isWeekend day) + putStrLn $ " Next Day: " ++ show (nextDay day) + putStrLn $ " Days until Friday: " ++ show (daysToParty day) + putStrLn "" + +-- Alternative implementations without Enum instance +-- nextDay :: DayOfWeek -> DayOfWeek +-- nextDay day = case day of +-- Monday -> Tuesday +-- Tuesday -> Wednesday +-- Wednesday -> Thursday +-- Thursday -> Friday +-- Friday -> Saturday +-- Saturday -> Sunday +-- Sunday -> Monday + +-- daysToParty :: DayOfWeek -> Int +-- daysToParty day = case day of +-- Monday -> 4 +-- Tuesday -> 3 +-- Wednesday -> 2 +-- Thursday -> 1 +-- Friday -> 0 +-- Saturday -> 6 +-- Sunday -> 5 + + {- =💣= Task 9* @@ -1019,3 +1278,5 @@ and summon @vrom911 for the review! Deriving: https://kowainik.github.io/posts/deriving Extensions: https://kowainik.github.io/posts/extensions -} + + diff --git a/src/Chapter4.hs b/src/Chapter4.hs index caec5a95d..50ddd0bc1 100644 --- a/src/Chapter4.hs +++ b/src/Chapter4.hs @@ -113,22 +113,28 @@ As always, try to guess the output first! And don't forget to insert the output in here: >>> :k Char +Char :: * >>> :k Bool +Bool :: * >>> :k [Int] +[Int] :: * >>> :k [] +[] :: * -> * >>> :k (->) - +(->) :: * -> * -> * >>> :k Either - +Either :: * -> * -> * >>> data Trinity a b c = MkTrinity a b c >>> :k Trinity +Trinity :: * -> * -> * -> * >>> data IntBox f = MkIntBox (f Int) >>> :k IntBox +IntBox :: (* -> *) -> * -} @@ -292,7 +298,9 @@ values and apply them to the type level? -} instance Functor (Secret e) where fmap :: (a -> b) -> Secret e a -> Secret e b - fmap = error "fmap for Box: not implemented!" + fmap _ (Trap e) = Trap e + fmap f (Reward a) = Reward (f a) + {- | =⚔️= Task 3 @@ -306,6 +314,12 @@ data List a = Empty | Cons a (List a) + +instance Functor List where + fmap :: (a -> b) -> List a -> List b + fmap _ Empty = Empty + fmap f (Cons x xs) = Cons (f x) (fmap f xs) + {- | =🛡= Applicative @@ -471,16 +485,19 @@ Implement the Applicative instance for our 'Secret' data type from before. -} instance Applicative (Secret e) where pure :: a -> Secret e a - pure = error "pure Secret: Not implemented!" + pure = Reward (<*>) :: Secret e (a -> b) -> Secret e a -> Secret e b - (<*>) = error "(<*>) Secret: Not implemented!" + (<*>) (Reward f) (Reward x) = Reward (f x) + (<*>) (Trap e) _ = Trap e + (<*>) _ (Trap e) = Trap e {- | =⚔️= Task 5 Implement the 'Applicative' instance for our 'List' type. + 🕯 HINT: in the applicative instance for lists, you have a list of functions and a list of arguments for those functions. You need to apply each function to each argument and combine all the results. You @@ -488,6 +505,13 @@ Implement the 'Applicative' instance for our 'List' type. type. -} +instance Applicative List where + pure :: a -> List a + pure x = Cons x Empty + + (<*>) :: List (a -> b) -> List a -> List b + Empty <*> _ = Empty + (Cons f fs) <*> xs = appendList (fmap f xs) (fs <*> xs) {- | =🛡= Monad @@ -599,7 +623,8 @@ Implement the 'Monad' instance for our 'Secret' type. -} instance Monad (Secret e) where (>>=) :: Secret e a -> (a -> Secret e b) -> Secret e b - (>>=) = error "bind Secret: Not implemented!" + (Trap e ) >>= _ = Trap e + (Reward a) >>= f = f a {- | =⚔️= Task 7 @@ -610,6 +635,16 @@ Implement the 'Monad' instance for our lists. maybe a few) to flatten lists of lists to a single list. -} +appendList :: List a -> List a -> List a +appendList Empty ys = ys +appendList (Cons x xs) ys = Cons x (appendList xs ys) + +instance Monad List where + (>>=) :: List a -> (a -> List b) -> List b + Empty >>= _ = Empty + (Cons x xs) >>= f = appendList (f x ) (xs >>= f) + + {- | =💣= Task 8*: Before the Final Boss