Switching type classes at runtime

From HaskellWiki
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

Introduction

It is possible to create a type class instance at runtime by coercing a value to another one. This blog post mentions the concept.

Tutorial

Let's say we have a type class called "Throws":

-- | Checked exceptions
class Throws e where
  throwChecked :: e -> IO a

We'd like to turn this into a "IOError", so that it can be caught:

-- | Rethrow checked exceptions as unchecked (regular) exceptions
rethrowUnchecked :: forall e a. (Throws e => IO a) -> (Exception e => IO a)
rethrowUnchecked act = aux act throwIO
  where
    aux :: (Throws e => IO a) -> ((e -> IO a) -> IO a)
    aux = unsafeCoerce . Wrap

-- | Wrap an action that may throw a checked exception
--
-- This is used internally in 'rethrowUnchecked' to avoid impredicative
-- instantiation of the type of 'unsafeCoerce'.
newtype Wrap e a = Wrap (Throws e => IO a)

-- | Catch a checked exception
--
-- This is the only way to discharge a 'Throws' type class constraint.
catchChecked :: Exception e => (Throws e => IO a) -> (e -> IO a) -> IO a
catchChecked = catch . rethrowUnchecked

-- | 'catchChecked' with the arguments reversed
handleChecked :: Exception e => (e -> IO a) -> (Throws e => IO a) -> IO a
handleChecked act handler = catchChecked handler act