Switching type classes at runtime
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