Skip to content

Commit fe30fb7

Browse files
committed
Added solutions for lesson 15
1 parent bf66bd3 commit fe30fb7

File tree

7 files changed

+229
-0
lines changed

7 files changed

+229
-0
lines changed

Homework15/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Revision history for Homework15
2+
3+
## 0.1.0.0 -- YYYY-mm-dd
4+
5+
* First version. Released on an unsuspecting world.

Homework15/Homework15.cabal

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
cabal-version: 3.0
2+
name: Homework15
3+
version: 0.1.0.0
4+
license: MIT
5+
license-file: LICENSE
6+
author: Robertino Martinez
7+
maintainer: robertino.martinez@iohk.io
8+
build-type: Simple
9+
extra-doc-files: CHANGELOG.md
10+
11+
common warnings
12+
ghc-options: -Wall
13+
14+
executable Homework15A
15+
import: warnings
16+
main-is: Homework15A.hs
17+
build-depends: base ^>=4.16.4.0
18+
hs-source-dirs: app
19+
default-language: Haskell2010
20+
21+
22+
library
23+
import: warnings
24+
build-depends: base ^>=4.16.4.0
25+
hs-source-dirs: src
26+
exposed-modules: Homework15B
27+
default-language: Haskell2010

Homework15/LICENSE

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Copyright (c) 2023 Robertino Martinez
2+
3+
Permission is hereby granted, free of charge, to any person obtaining
4+
a copy of this software and associated documentation files (the
5+
"Software"), to deal in the Software without restriction, including
6+
without limitation the rights to use, copy, modify, merge, publish,
7+
distribute, sublicense, and/or sell copies of the Software, and to
8+
permit persons to whom the Software is furnished to do so, subject to
9+
the following conditions:
10+
11+
The above copyright notice and this permission notice shall be included
12+
in all copies or substantial portions of the Software.
13+
14+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Homework15/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
## Homework
3+
4+
1. Read the [Data.Maybe](https://hackage.haskell.org/package/base-4.18.0.0/docs/Data-Maybe.html) and [Data.Either](https://hackage.haskell.org/package/base-4.18.0.0/docs/Data-Either.html) modules.
5+
2. Read the [Control.Exception](https://hackage.haskell.org/package/base-4.18.0.0/docs/Control-Exception.html) module skipping the "Asynchronous Exceptions" section.
6+
3. Solve Homework15A (inside `app` directory) and Homework15B (inside `src`).

Homework15/app/Homework15A.hs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
module Main where
2+
3+
import Control.Exception (catch, try)
4+
import Text.Read (readMaybe)
5+
6+
--------------------------------------------------------------------------------
7+
--------------------------------------------------------------------------------
8+
-- IMPORTANT: Read the README.md file before completing the homework.
9+
--------------------------------------------------------------------------------
10+
--------------------------------------------------------------------------------
11+
12+
-- This is a CLI application that allows the user to manage a TODO list.
13+
-- The user can add and remove items from the list and save and load the list
14+
-- from a file.
15+
-- It's a working prototype, but it has some bugs. Specifically, it doesn't
16+
-- handle errors very well. Your mission, should you choose to accept it, is to
17+
-- fix those bugs and make the application more robust. Hint: Try to interact
18+
-- with the application in unexpected ways and see what happens! You should be
19+
-- able to find and fix 3 bugs.
20+
21+
printTodoItem :: (Int, String) -> IO ()
22+
printTodoItem (n, todo) = putStrLn (show n ++ ": " ++ todo)
23+
24+
prompt :: [String] -> IO ()
25+
prompt todos = do
26+
putStrLn ""
27+
putStrLn "Current TODO list:"
28+
foldr (\x k -> printTodoItem x >> k) (return ()) (zip [0 ..] todos)
29+
command <- getLine
30+
interpretCommand command todos
31+
32+
delete :: Maybe Int -> [a] -> [a]
33+
delete Nothing as = as
34+
delete _ [] = []
35+
delete (Just 0) (_ : as) = as
36+
delete (Just n) (a : as) = a : delete (Just (n - 1)) as
37+
38+
interpretCommand :: String -> [String] -> IO ()
39+
interpretCommand cmd todos = case cmd of
40+
"q" -> return ()
41+
('+' : ' ' : todo) -> prompt (todo : todos)
42+
('-' : ' ' : num) -> prompt $ delete (readMaybe num) todos
43+
('s' : ' ' : fn) ->
44+
writeFile fn (show todos) `catch` \e -> do
45+
putStrLn $ "Could not write to file because: " ++ show (e :: IOError)
46+
prompt todos
47+
-- There's no reason as to why this and the previous case should handle
48+
-- errors differently. I did it this way so you can see how to handle errors
49+
-- in different ways.
50+
('l' : ' ' : fn) -> do
51+
contents <- try $ readFile fn >>= return . read
52+
case contents of
53+
Left e -> do
54+
putStrLn $ "Could not read from file because: " ++ show (e :: IOError)
55+
prompt todos
56+
Right todos' -> prompt todos'
57+
_ -> do
58+
putStrLn ("Invalid command: `" ++ cmd ++ "`")
59+
prompt todos
60+
61+
printCommands :: IO ()
62+
printCommands = do
63+
putStrLn "Commands:"
64+
putStrLn "+ <Item Name> - Add a TODO entry"
65+
putStrLn "- <Item Number> - Delete the numbered entry"
66+
putStrLn "s <File Name> - Save the current list of TODOs"
67+
putStrLn "l <File Name> - Load the saved list of TODOs"
68+
putStrLn "q - Quit without saving"
69+
70+
main :: IO ()
71+
main = do
72+
printCommands
73+
prompt []

Homework15/hye.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
cradle:
2+
cabal:
3+

Homework15/src/Homework15B.hs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
{-# LANGUAGE ScopedTypeVariables #-}
2+
3+
module Homework15B where
4+
5+
import Control.Exception (IOException, try)
6+
7+
--------------------------------------------------------------------------------
8+
--------------------------------------------------------------------------------
9+
-- IMPORTANT: Read the README.md file before completing the homework.
10+
--------------------------------------------------------------------------------
11+
--------------------------------------------------------------------------------
12+
13+
-- 1. Write a function that takes a list and returns the head if the list is not empty.
14+
-- If the list is empty, return Nothing.
15+
16+
headMaybe :: [a] -> Maybe a
17+
headMaybe [] = Nothing
18+
headMaybe (x : _) = Just x
19+
20+
-- 2. Write a function that takes a list of Maybe values and returns a list of all the Just values.
21+
-- If there are no Just values, return an empty list.
22+
23+
catMaybes :: [Maybe a] -> [a]
24+
catMaybes [] = []
25+
catMaybes (Nothing : xs) = catMaybes xs
26+
catMaybes (Just x : xs) = x : catMaybes xs
27+
28+
-- 3. Write a function that tries to read from a file and returns the contents of the file.
29+
-- If the file does not exist, return Nothing.
30+
31+
readFileMaybe :: FilePath -> IO (Maybe String)
32+
readFileMaybe path = do
33+
contents <- try $ readFile path
34+
case contents of
35+
Left (_ :: IOException) -> return Nothing
36+
Right c -> return (Just c)
37+
38+
-- 4. Write a function that checks all the requirements for a password using the
39+
-- Either type with a custom data type for errors.
40+
-- The requirements are:
41+
-- - The password must be at least 10 characters long.
42+
-- - The password must contain at least one digit.
43+
-- - The password must contain at least one uppercase letter.
44+
-- - The password must contain at least one lowercase letter.
45+
46+
data PasswordError
47+
= NotLongEnough
48+
| NoDigit
49+
| NoUppercase
50+
| NoLowercase
51+
deriving (Eq, Show)
52+
53+
passwordLongEnough :: String -> Either PasswordError String
54+
passwordLongEnough password =
55+
if length password >= 10
56+
then Right password
57+
else Left NotLongEnough
58+
59+
passwordHasDigit :: String -> Either PasswordError String
60+
passwordHasDigit password =
61+
if any (`elem` ['0' .. '9']) password
62+
then Right password
63+
else Left NoDigit
64+
65+
passwordHasUppercase :: String -> Either PasswordError String
66+
passwordHasUppercase password =
67+
if any (`elem` ['A' .. 'Z']) password
68+
then Right password
69+
else Left NoUppercase
70+
71+
passwordHasLowercase :: String -> Either PasswordError String
72+
passwordHasLowercase password =
73+
if any (`elem` ['a' .. 'z']) password
74+
then Right password
75+
else Left NoLowercase
76+
77+
--------------------------------------------------------------------------------
78+
--------------------------------------------------------------------------------
79+
-- As you can see, the passwordRequirements function is very repetitive.
80+
-- This is one of the downsides of using nested optional values.
81+
-- We're going to solve this problem in the "Basic Abstractions" section of the coruse.
82+
--------------------------------------------------------------------------------
83+
--------------------------------------------------------------------------------
84+
85+
passwordRequirements :: String -> Either PasswordError String
86+
passwordRequirements password =
87+
case passwordLongEnough password of
88+
Left err -> Left err
89+
Right _ -> case passwordHasDigit password of
90+
Left err -> Left err
91+
Right _ -> case passwordHasUppercase password of
92+
Left err -> Left err
93+
Right _ -> case passwordHasLowercase password of
94+
Left err -> Left err
95+
Right _ -> Right password

0 commit comments

Comments
 (0)