A Brief Look at Asynchronous Exceptions

We've covered a lot of different exception mechanisms this month. Today we'll cover just one more concept that will serve as a teaser for some future exploration of concurrency. All the exception mechanisms we've looked at so are serial in nature. We call a certain IO function from our main thread, and we block the program until that operation finishes. However, exceptions can also happen in an asynchronous way. This can happen in a couple different ways.

First, as we'll learn later this year, we can fork different threads for our program to run, and it is possible to raise an exception in a different thread! If you're interested in exploring how this works, the relevant functions you should learn about are forkIO and throwTo:

forkIO :: IO () -> IO ThreadId

throwTo :: (Exception e) => ThreadId -> e -> IO ()

The first will take an IO action and run it on a new thread, returning the ID of that thread. The section function then allows you to raise an exception in that thread using the ID. We'll look into the details of this function at a later date.

However, there are also certain asynchronous exceptions that can occur no matter what we do. At any point, our program could theoretically run out of memory (StackOverflow or HeapOverflow), and our program will have to abort the execution of our program in an asynchronous manner. So even if we aren't specially forking new threads, we still might encounter these kinds of exceptions.

The mask function is a special utility that prevents an action from being interrupted by an asynchronous exception.

mask :: ((forall a. IO a -> IO a) -> IO b) -> IO b

However, just from looking at the type signature, this function is rather confusing. It takes one argument, a function that takes another function as its input! As we can read in the documentation though, the most common use case for this function is to protect a resource to make sure we release it even if an exception is thrown while performing a computation with it.

Lucky for us, the bracket function already handles this case, as we've discussed. So the chances that you'll have to manually use mask are not very high.

This wraps up our discussion of exceptions for now on Monday Morning Haskell! We'll be sure to touch on this subject again when we get to concurrency in a few months. If you want a summary of this month's articles in your inbox, there's still time to subscribe to our monthly newsletter! You won't want to miss what's coming up next week!

Previous
Previous

10 Steps to Understanding Data Structures in Haskell

Next
Next

Catching Before Production: Assert Statements in Haskell