From 66207ec8b492324d8a664a3f4ce46b7af478413b Mon Sep 17 00:00:00 2001 From: JadAbouHawili Date: Wed, 18 Jun 2025 11:03:19 +0000 Subject: [PATCH 1/3] wrong import for catch Should import Control.Exception , not System.IO.Error Signed-off-by: JadAbouHawili --- docs/input-and-output.html | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/input-and-output.html b/docs/input-and-output.html index c5f390d..ec4214f 100644 --- a/docs/input-and-output.html +++ b/docs/input-and-output.html @@ -1199,16 +1199,14 @@

Input and Output

We did fileExists <- doesFileExist fileName because doesFileExist has a type of doesFileExist :: FilePath -> IO Bool, which means that it returns an I/O action that has as its result a boolean value which tells us if the file exists. We can't just use doesFileExist in an if expression directly.

Another solution here would be to use exceptions. It's perfectly acceptable to use them in this context. A file not existing is an exception that arises from I/O, so catching it in I/O is fine and dandy.

-

To deal with this by using exceptions, we're going to take advantage of the catch function from System.IO.Error. Its type is catch :: IO a -> (IOError -> IO a) -> IO a. It takes two parameters. The first one is an I/O action. For instance, it could be an I/O action that tries to open a file. The second one is the so-called handler. If the first I/O action passed to catch throws an I/O exception, that exception gets passed to the handler, which then decides what to do. So the final result is an I/O action that will either act the same as the first parameter or it will do what the handler tells it if the first I/O action throws an exception.

+

To deal with this by using exceptions, we're going to take advantage of the catch function from Control.Exception. Its type is catch :: IO a -> (IOError -> IO a) -> IO a. It takes two parameters. The first one is an I/O action. For instance, it could be an I/O action that tries to open a file. The second one is the so-called handler. If the first I/O action passed to catch throws an I/O exception, that exception gets passed to the handler, which then decides what to do. So the final result is an I/O action that will either act the same as the first parameter or it will do what the handler tells it if the first I/O action throws an exception.

non sequitur

If you're familiar with try-catch blocks in languages like Java or Python, the catch function is similar to them. The first parameter is the thing to try, kind of like the stuff in the try block in other, imperative languages. The second parameter is the handler that takes an exception, just like most catch blocks take exceptions that you can then examine to see what happened. The handler is invoked if an exception is thrown.

The handler takes a value of type IOError, which is a value that signifies that an I/O exception occurred. It also carries information regarding the type of the exception that was thrown. How this type is implemented depends on the implementation of the language itself, which means that we can't inspect values of the type IOError by pattern matching against them, just like we can't pattern match against values of type IO something. We can use a bunch of useful predicates to find out stuff about values of type IOError as we'll learn in a second.

So let's put our new friend catch to use!

 import System.Environment
-import System.IO
-import System.IO.Error
-
+import Control.Exception
 main = toTry `catch` handler
 
 toTry :: IO ()

From 129089e72d916dbc7ba833c9419060429f76a5be Mon Sep 17 00:00:00 2001
From: JadAbouHawili 
Date: Wed, 18 Jun 2025 11:47:28 +0000
Subject: [PATCH 2/3] correcting imports

need Control.Exception for catch , System.IO.Error for IOError

Signed-off-by: JadAbouHawili 
---
 docs/input-and-output.html | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/docs/input-and-output.html b/docs/input-and-output.html
index ec4214f..5bdc220 100644
--- a/docs/input-and-output.html
+++ b/docs/input-and-output.html
@@ -1229,8 +1229,9 @@ 

Input and Output

In the handler, we didn't check to see what kind of IOError we got. We just say "Whoops, had some trouble!" for any kind of error. Just catching all types of exceptions in one handler is bad practice in Haskell just like it is in most other languages. What if some other exception happens that we don't want to catch, like us interrupting the program or something? That's why we're going to do the same thing that's usually done in other languages as well: we'll check to see what kind of exception we got. If it's the kind of exception we're waiting to catch, we do our stuff. If it's not, we throw that exception back into the wild. Let's modify our program to catch only the exceptions caused by a file not existing.

 import System.Environment
-import System.IO
-import System.IO.Error
+import System.IO.Error  
+import Control.Exception
+
 
 main = toTry `catch` handler
 

From d8ad4bc017691411944d29cab95ce9941aeabf32 Mon Sep 17 00:00:00 2001
From: JadAbouHawili 
Date: Wed, 18 Jun 2025 12:06:09 +0000
Subject: [PATCH 3/3] another import correction

Signed-off-by: JadAbouHawili 
---
 docs/input-and-output.html | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/docs/input-and-output.html b/docs/input-and-output.html
index 5bdc220..c38440d 100644
--- a/docs/input-and-output.html
+++ b/docs/input-and-output.html
@@ -1272,8 +1272,9 @@ 

Input and Output

System.IO.Error also exports functions that enable us to ask our exceptions for some attributes, like what the handle of the file that caused the error is, or what the filename is. These start with ioe and you can see a full list of them in the documentation. Say we want to print the filename that caused our error. We can't print the fileName that we got from getArgs, because only the IOError is passed to the handler and the handler doesn't know about anything else. A function depends only on the parameters it was called with. That's why we can use the ioeGetFileName function, which has a type of ioeGetFileName :: IOError -> Maybe FilePath. It takes an IOError as a parameter and maybe returns a FilePath (which is just a type synonym for String, remember, so it's kind of the same thing). Basically, what it does is it extracts the file path from the IOError, if it can. Let's modify our program to print out the file path that's responsible for the exception occurring.

 import System.Environment
-import System.IO
 import System.IO.Error
+import Control.Exception
+
 
 main = toTry `catch` handler