{-| The Build type, and associated operations. -} module Network.Haskoin.Util.BuildMonad ( -- *Build monad Build(..) , isComplete , isPartial , isBroken , eitherToBuild , buildToEither , guardPartial -- *BuildT transformer monad , BuildT(..) , liftBuild ) where import Control.Monad (liftM) import Control.Monad.Trans ( MonadTrans , MonadIO , lift , liftIO ) {-| The Build monad represents computations that can be in one of three states: * Complete * Partial * Broken It extends the Either monad with an additional Partial value to describe a valid computation flagged with a Partial context. The Build monad is useful when you describe computations where parts of the computation are either complete, partially complete or broken. Combining only Complete computations will produce a Complete result. However, if one of the computations is Partial, the whole computation will be Partial as well. And if some computation is Broken, the whole computation will be broken as well. The Build monad is used by Haskoin to describe the state of the transaction signing computation. To sign a transaction, all input scripts need to be signed. The whole transaction will be completely signed only if all the input scripts are completely signed. If any of the inputs is partially signed, then the whole transaction will be partially signed as well. And the whole transaction is broken if one of the inputs failed to parse or is broken. -} data Build a -- | Describes a successful complete computation = Complete { runBuild :: a } -- | Describes a successful but partial computation | Partial { runBuild :: a } -- | Describes a broken computation | Broken { runBroken :: String } deriving Eq instance Show a => Show (Build a) where show (Complete a) = "Complete " ++ (show a) show (Partial a) = "Partial " ++ (show a) show (Broken str) = "Broken " ++ str instance Functor Build where fmap f (Complete x) = Complete (f x) fmap f (Partial x) = Partial (f x) fmap _ (Broken s) = Broken s instance Monad Build where return = Complete Complete x >>= f = f x Partial x >>= f = case f x of e@(Broken _) -> e a -> Partial $ runBuild a Broken s >>= _ = Broken s -- | Returns True if the Build value is Complete isComplete :: Build a -> Bool isComplete (Complete _) = True isComplete _ = False -- | Returns True if the Build value is Partial isPartial :: Build a -> Bool isPartial (Partial _) = True isPartial _ = False -- | Return True if the Build value is Broken isBroken :: Build a -> Bool isBroken (Broken _) = True isBroken _ = False -- | Transforms an Either String value into a Build value. Right is mapped to -- Complete and Left is mapped to Broken eitherToBuild :: Either String a -> Build a eitherToBuild m = case m of Left err -> Broken err Right res -> Complete res -- | Transforms a Build value into an Either String value. Complete and Partial -- are mapped to Right and Broken is mapped to Left. buildToEither :: Build a -> Either String a buildToEither m = case m of Complete a -> Right a Partial a -> Right a Broken err -> Left err -- | Binds a Partial value to the computation when the predicate is False. guardPartial :: Bool -> Build () guardPartial True = Complete () guardPartial False = Partial () -- | BuildT transformer monad newtype BuildT m a = BuildT { runBuildT :: m (Build a) } mapBuildT :: (m (Build a) -> n (Build b)) -> BuildT m a -> BuildT n b mapBuildT f = BuildT . f . runBuildT instance Functor m => Functor (BuildT m) where fmap f = mapBuildT (fmap (fmap f)) instance Monad m => Monad (BuildT m) where return = lift . return x >>= f = BuildT $ do v <- runBuildT x case v of Complete a -> runBuildT (f a) Partial a -> runBuildT (f a) Broken str -> return $ Broken str instance MonadTrans BuildT where lift = BuildT . liftM Complete instance MonadIO m => MonadIO (BuildT m) where liftIO = lift . liftIO -- | Lift a Build computation into the BuildT monad liftBuild :: Monad m => Build a -> BuildT m a liftBuild = BuildT . return