{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TemplateHaskell #-} {- Copyright (C) 2009 John Goerzen All rights reserved. For license and copyright information, see the file COPYRIGHT -} {- | Module : Data.Convertible.Base Copyright : Copyright (C) 2009 John Goerzen License : LGPL Maintainer : Michael Snoyman Stability : provisional Portability: portable -} module Data.Convertible.Base( ConvertAttempt (..), failure, ConvertSuccess (..), cs, ca, ConversionException (..), convertUnsafe, convertAttemptWrap, deriveAttempts ) where import Data.Attempt import Control.Exception (Exception) import Data.Typeable (Typeable) import Language.Haskell.TH ---------------------------------------------------------------------- -- Conversions ---------------------------------------------------------------------- {- | A typeclass that represents something that can attempt a conversion. An @ConvertAttempt a b@ instance represents an @a@ that might be convertible to a @b@. -} class ConvertAttempt a b where {- | Convert @a@ to @b@, returning 'Success' on success and 'Failure' on error. -} convertAttempt :: a -> Attempt b -- | A convenience synonym for 'convertAttempt' ca :: ConvertAttempt x y => x -> Attempt y ca = convertAttempt {- | A typeclass that represents something that guarantees a successful conversion. A @ConvertSuccess a b@ instance represents an @a@ that can be converted to a @b@. -} class ConvertSuccess a b where {- | Convert @a@ to @b@. -} convertSuccess :: a -> b -- | A convenience synonym for 'convertSuccess' cs :: ConvertSuccess x y => x -> y cs = convertSuccess {- | Any type can be converted to itself. -} instance ConvertSuccess a a where convertSuccess = id instance ConvertAttempt a a where convertAttempt = return {- FIXME consider exposing this {- | Lists of any convertible type can be converted. -} instance Convertible a b => Convertible [a] [b] where safeConvert = mapM safeConvert -} {- | Convert from one type of data to another. Raises an exception if there is an error with the conversion. For a function that does not raise an exception in that case, see 'convertAttempt'. -} convertUnsafe :: ConvertAttempt a b => a -> b convertUnsafe = fromSuccess . convertAttempt {- | Wraps any 'Exception' which could occur during a 'convertAttempt'. -} data ConversionException = forall e. Exception e => ConversionException e deriving Typeable instance Show ConversionException where show (ConversionException e) = "ConversionException " ++ show e instance Exception ConversionException {- | Calls 'convertAttempt', wrapping any 'Exception's in a 'ConversionException' -} convertAttemptWrap :: (ConvertAttempt a b, Failure ConversionException m ) => a -> m b convertAttemptWrap = attempt (failure . ConversionException) return . convertAttempt convertAttempt' :: ConvertSuccess x y => x -> Attempt y convertAttempt' = return . convertSuccess -- | Template Haskell to derive 'ConvertAttempt' instances from the -- corresponding 'ConvertSuccess' instances. deriveAttempts :: [(Name, Name)] -> Q [Dec] deriveAttempts pairs = do ca' <- [|convertAttempt'|] return $ map (helper ca') pairs where helper ca' (x, y) = InstanceD [] (ConT (mkName "ConvertAttempt") `AppT` ConT x `AppT` ConT y) [ FunD (mkName "convertAttempt") [ Clause [] (NormalB ca') [] ] ]