Last active
January 16, 2022 20:45
-
-
Save srithon/9fd11424155255a6bf2e642dae03c079 to your computer and use it in GitHub Desktop.
A basic TODO list application written in Haskell.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Data.Maybe | |
import System.IO | |
type Todos = [Todo] | |
type Todo = String | |
addTodo :: Todos -> Todo -> Todos | |
addTodo todos todo = todos ++ [todo] | |
removeTodoN :: Todos -> Int -> Todos | |
-- NOTE: remember to account for empty patterns | |
removeTodoN [] i = [] | |
removeTodoN list@(t : ts) i | |
| i < 1 = list | |
| i == 1 = ts | |
| otherwise = t : removeTodoN ts (i - 1) | |
removeTodoMatching :: Todos -> Todo -> Todos | |
removeTodoMatching [] s = [] | |
removeTodoMatching (t : ts) s | |
| s == t = ts | |
| otherwise = t : removeTodoMatching ts s | |
readTodos :: FilePath -> IO Todos | |
readTodos path = do | |
contents <- readFile path | |
return $ lines contents | |
displayTodos :: Todos -> IO () | |
displayTodos todos = | |
mapM_ putStrLn $ | |
-- NOTE: use zipWith rather than map . zip | |
zipWith | |
(\i t -> show i ++ ". " ++ t) | |
[1 ..] | |
todos | |
data Action = Remove Int | Add Todo | Exit | |
parseUserAction' :: String -> Maybe Action | |
parseUserAction' input | |
| null inputWords = Just Exit | |
| action == "remove" = | |
if numWords > 1 | |
then Just $ Remove $ read $ inputWords !! 1 | |
else Nothing | |
| action == "add" = Just $ Add $ unwords $ tail inputWords | |
| otherwise = Nothing | |
where | |
inputWords = words input | |
numWords = length inputWords | |
action = head inputWords | |
parseUserAction :: String -> IO Action | |
-- if `parseUseraction' <input>` yields Nothing, yields the result of | |
-- `getUserAction`; else, uses `return` to map the `Just a` (Action) value to | |
-- an `IO Action` | |
parseUserAction = maybe getUserAction return . parseUserAction' | |
getUserAction :: IO Action | |
getUserAction = do | |
putStrLn "\nOptions: remove `i`, add `str` or empty to exit" | |
-- source: https://stackoverflow.com/questions/55091150/change-exit-value-on-getline-exception-in-haskell | |
-- | |
-- getLine will panic if it sees an EOF, so we need to check if there's an EOF beforehand and handle it accordingly | |
isClosed <- isEOF | |
if isClosed | |
then return Exit | |
else do | |
input <- getLine | |
parseUserAction input | |
run :: Todos -> IO Todos | |
run todos = do | |
putStrLn "These are your existing TO-DO items:" | |
-- display the todos | |
displayTodos todos | |
-- ask user what they want to do | |
action <- getUserAction | |
case action of | |
Remove i -> run $ removeTodoN todos i | |
Add s -> run $ addTodo todos s | |
Exit -> return todos | |
main = do | |
todos <- readTodos "./todo.txt" | |
newTodos <- run todos | |
writeFile "./todo.txt" $ unlines newTodos |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment