6

The type of handleError function is

:: (e -> Contract w s e' a)-> Contract w s e a -> Contract w s e' a

It takes two arguments

  • a function which takes error and returns a Contract monad (Contract w s e' a) without error
  • Contract Monad with error

and returns a Contract Monad without error (Contract w s e a). This is what I could understand. However when this is used in the following code

payContract = do
   pp <- endpoint @"pay"
   let tx = mustPayToPubKey (ppRecipient pp) $ lovelaceValueOf $ ppLovelace pp
   handleError (\err -> Contract.logInfo $ "caught error: " ++ unpack err) $ void $ submitTx tx
   payContract

In this function call submitTx Tx gives the Contract Monad with error and the lambda function \err -> Contract.logInfo $ "caught error: " ++ unpack err may be the first argument. What is happening to the returned Monad without error? and why void is used?

3

Definitions:

submitTx :: forall w s e. (HasWriteTx s, AsContractError e) => TxConstraints Void Void -> Contract w s e Tx

void :: Functor f => f a -> f ()

handleError :: (e -> Contract w s e' a)-> Contract w s e a -> Contract w s e' a

Explaination:

submitTx tx is of type Contract w s e Tx, i.e. a Contract Monad with return type Tx.

void takes any Monad and maps it to a Monad with return type (). This is useful if you don't care for the returned value: void $ submitTx tx :: Contract w s e ()

handleError then takes the Monad and handles any errors with the specified error handler (e -> Contract w s e' a). However, it doesn't change the return type of the Contract Monad. In case there is no error on runtime the return value is just passed through.

payContract calls itself recursively on the last line of the do block in order to listen to the pay-endpoint indefinitely. Let's assume you'd leave out handleError and only use void $ submitTx tx your contract would crash on the first submission error.

If you wouldn't use void, your program would still be valid, but your compiler would output a warning, because you didn't use the return value of submitTx tx.

2

What is happening to the returned Monad?

All of this takes place within the do block. So, similar to the

<- endpoint @"pay"

the returned Monad is being abstracted away for you. The whole function is returning a Contract, so if it fails on either line, it will exit "early" and return the failed value. The reason it's not using the <- symbol is the answer to your second question:

why void is used?

We don't need the contained value.

void :: Functor f => f a -> f () 

So, void for Contract converts Contract w s e a to Contract w s e ().


You could also do this conversion by ignoring the output of a monad, as long as you still return the correct a for Contract, which in this case is ().

_ <- submit tx
return ()

is equivalent to

void $ submit tx

However, void is more explicit and fits better with the

handleError (\err -> Contract.logInfo $ "caught error: " ++ unpack err)

in your example.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.