In the solution for the Lecture #4 (cohort 1 I guess, I'm following on Youtube)

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

It appears that payContract calls itself, and this is required to get the payTest1 and payTest2 functions to actually run.

My question is: how does this not recurse infinitely? Where is the condition that stops it?

I'm still learning Haskell as I go and I come from a C# background, so I'm still getting my head around the functional programming thing.


2 Answers 2


payContract blocks on the first line of the do block until the pay endpoint is invoked. The input of the invocation is put in pp.

pp <- endpoint @"pay"

It then executes the endpoint invocation by creating the transaction and submitting it, and returns to the blocked state waiting for the next invocation.

It does recurse indefinitely, thus ensuring that the contract endpoints are available.

  • 1
    Thanks @vcwebco - that makes sense. When I am running the Emulator from the cmd line by calling payTest1: payTest1 :: IO () payTest1 = runEmulatorTraceIO $ payTrace 1000000 2000000 does the payContract fn keep running while it's blocked listening for the endpoint call in a separate thread even after payTrace returns? How does the Emulator know to stop listening for endpoint calls?
    – marcel_g
    Aug 4, 2021 at 4:16
  • 1
    @marcel_g - contracts get activated within the context of a wallet, e.g in payTrace with code line h <- activateContractWallet (Wallet 1) payContract. That makes the endpoint(s) available for invocation, e.g. with callEndpoint @"pay" h $ PayParams. Note that activation is not global. The visual representation in the playground simulator shows wallets with the contract already activated (buttons), and with the ability to add more.
    – vcwebco
    Aug 4, 2021 at 12:53

Simply put, it could recurse infinitely.

Haskell doesn't have loops, so this is the equivalent of a while loop or something, where the exit case is the Contract monad throwing an error.

If you want to learn more, check out this page on tail recursion in Haskell.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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