Timeouts in PureScript

module Main where

import Prelude
import Effect (Effect)
import Effect.Console (log)
import Effect.Aff (Aff, launchAff_, delay)
import Effect.Class (liftEffect)
import Data.Time.Duration (Milliseconds(..))
import Control.Alt ((<|>))

In PureScript, we use the Effect monad for side effects and the Aff monad for asynchronous operations. We’ll use these to simulate timeouts and asynchronous operations.

main :: Effect Unit
main = do
  launchAff_ do
    -- For our example, suppose we're executing an external
    -- call that returns its result after 2s.
    result1 <- timeout 1000 $ delay (Milliseconds 2000) *> pure "result 1"
    case result1 of
      Just res -> liftEffect $ log res
      Nothing -> liftEffect $ log "timeout 1"

    -- If we allow a longer timeout of 3s, then the operation
    -- will succeed and we'll print the result.
    result2 <- timeout 3000 $ delay (Milliseconds 2000) *> pure "result 2"
    case result2 of
      Just res -> liftEffect $ log res
      Nothing -> liftEffect $ log "timeout 2"

-- Helper function to implement timeout
timeout :: Int -> Aff String -> Aff (Maybe String)
timeout ms aff = (Just <$> aff) <|> (delay (Milliseconds $ toNumber ms) *> pure Nothing)

In this PureScript version:

  1. We use launchAff_ to run our asynchronous operations.
  2. The delay function from Effect.Aff is used to simulate long-running operations.
  3. We implement a timeout function that uses the Alt instance of Aff to race between the given operation and a delay.
  4. Instead of channels, we use Aff for asynchronous operations and the Maybe type to represent the possibility of a timeout.

To run this program:

$ spago run
timeout 1
result 2

This output shows the first operation timing out and the second succeeding, just like in the original example.

In PureScript, we don’t have built-in channels or goroutines, but we can achieve similar functionality using the Aff monad and its combinators. The timeout function acts similarly to the select statement in the original example, allowing us to race between an operation and a timeout.