Switching type classes at runtime

From HaskellWiki
Jump to navigation Jump to search

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