Building Interactive Programs
Introduction to Interactive Haskell
Welcome back! In our previous lessons, we built a solid foundation of pure functions for our task manager. We created custom data types, modeled missing data with Maybe, and handled errors with Either. These pure functions, like our lookupById function, safely calculate results based on the data we feed them.
However, pure functions just sit there quietly. They calculate answers, but they do not actively communicate with the user. To make a real, usable application, we need to interact with the user. We need to wait for them to type a command, read what they typed, and print a response to the screen.
In Haskell, we do this using IO (Input/Output). IO is how Haskell safely interacts with the outside world while keeping the rest of our logic pure and predictable. In this lesson, we are going to build an interactive shell that wraps around the pure task manager logic you already wrote.
Parsing Input: words and readMaybe
Before we ask the user to type something, we need a plan to understand what they type. Imagine the user wants to look up task number 1. They might type a command like "find 1".
First, we need to split this single string into separate pieces. We can do this using a built-in function called words. It takes a sentence and breaks it into a list of individual words.
Now we have a list of strings, but our task lookup functions require a number (an Int). We need to convert the string "1" into the number 1. You might remember from earlier lessons that assuming data is always correct can crash our program. If the user types "find apples", trying to force "apples" into a number will cause an error.
To solve this, we will import a safe tool called readMaybe from the Text.Read module. Instead of crashing on bad text, readMaybe returns a Maybe type. This should look familiar from Unit 4!
With these tools, we can write pure functions that decide how to reply to the user's text. Let's write a function called parseFind. It will look at the Maybe value we get from readMaybe. If it sees Nothing, it will politely ask for a number. If it sees Just tid, it will pass that ID to our existing lookupById function.
Next, we write a respond function. This function takes our list of tasks and the list of words the user typed. We will use pattern matching to check if the first word is "find" and pass the second word to parseFind.
If the user types exactly "find" followed by another word, we process it. If they type anything else, we catch it with a fallback pattern _ _ and show them a helpful hint.
