Personal tools

Switching type classes at runtime

From HaskellWiki

Jump to: navigation, search

1 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.

2 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