Switching type classes at runtime
Revision as of 10:06, 3 August 2015 by Beerdude26 (Created page)
It is possible to create a type class instance at runtime by coercing a value to another one. This blog post mentions the concept.
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