Online Book Reader

Home Category

Learn You a Haskell for Great Good! - Miran Lipovaca [82]

By Root 450 0
tasks and an entirely separate program for deleting them. Now we’re going to join that into a single program, and whether it adds or deletes items will depend on the command-line arguments we pass to it. We’ll also make it able to operate on different files, not just todo.txt.

We’ll call our program todo, and it will be able to do three different things:

View tasks

Add tasks

Delete tasks

To add a task to the todo.txt file, we enter it at the terminal:

$ ./todo add todo.txt "Find the magic sword of power"

To view the tasks, we enter the view command:

$ ./todo view todo.txt

To remove a task, we use its index:

$ ./todo remove todo.txt 2

A Multitasking Task List


We’ll start by making a function that takes a command in the form of a string, like "add" or "view", and returns a function that takes a list of arguments and returns an I/O action that does what we want:

import System.Environment

import System.Directory

import System.IO

import Data.List

dispatch :: String -> [String] -> IO ()

dispatch "add" = add

dispatch "view" = view

dispatch "remove" = remove

We’ll define main like this:

main = do

(command:argList) <- getArgs

dispatch command argList

First, we get the arguments and bind them to (command:argList). This means that the first argument will be bound to command, and the rest of the arguments will be bound to argList. In the next line of our main block, we apply the dispatch function to the command, which results in the add, view, or remove function. We then apply that function to argList.

Suppose we call our program like this:

$ ./todo add todo.txt "Find the magic sword of power"

command is "add", and argList is ["todo.txt", "Find the magic sword of power"]. That way, the second pattern match of the dispatch function will succeed, and it will return the add function. Finally, we apply that to argList, which results in an I/O action that adds the item to our to-do list.

Now let’s implement the add, view, and remove functions. Let’s start with add:

add :: [String] -> IO ()

add [fileName, todoItem] = appendFile fileName (todoItem ++ "\n")

We might call our program like so:

./todo add todo.txt "Find the magic sword of power"

The "add" will be bound to command in the first pattern match in the main block, whereas ["todo.txt", "Find the magic sword of power"] will be passed to the function that we get from the dispatch function. So, because we’re not dealing with bad input right now, we just pattern match against a list with those two elements immediately and return an I/O action that appends that line to the end of the file, along with a newline character.

Next, let’s implement the list-viewing functionality. If we want to view the items in a file, we do ./todo view todo.txt. So in the first pattern match, command will be "view", and argList will be ["todo.txt"]. Here’s the function in full:

view :: [String] -> IO ()

view [fileName] = do

contents <- readFile fileName

let todoTasks = lines contents

numberedTasks = zipWith (\n line -> show n ++ " - " ++ line)

[0..] todoTasks

putStr $ unlines numberedTasks

When we made our deletetodo program, which could only delete items from a to-do list, it had the ability to display the items in a to-do list, so this code is very similar to that part of the previous program.

Finally, we’re going to implement remove. It’s very similar to the program that only deleted the tasks, so if you don’t understand how deleting an item here works, review Deleting Items in Deleting Items. The main difference is that we’re not hardcoding the filename as todo.txt but instead getting it as an argument. We’re also getting the target task number as an argument, rather than prompting the user for it.

remove :: [String] -> IO ()

remove [fileName, numberString] = do

contents <- readFile fileName

let todoTasks = lines contents

numberedTasks = zipWith (\n line -> show n ++ " - " ++ line)

[0..] todoTasks

putStrLn "These are your TO-DO items:"

mapM_ putStrLn numberedTasks

let number = read numberString

newTodoItems = unlines $ delete (todoTasks

Return Main Page Previous Page Next Page

®Online Book Reader