1
votes

I have the following programm written with Effs and Affs. Which runs as expected. That is it prints out the given Int and it does an asynchronous computation.

type JsonResponse = AffjaxResponse Json
access :: forall e m. Aff (ajax :: AJAX | e) (Either Error JsonResponse)
access = attempt $ get "http://localhost:8080/livesys/Robert"

staging :: forall e. Int -> Eff (console :: CONSOLE | e) Int
staging i = do
    liftEff $ log $ ">>" ++ show i
    return i

main :: forall a. Int -> Aff (ajax :: AJAX, console :: CONSOLE| a) Int
main state = do
    s <- liftEff $ staging state
    a <- liftAff access
    return s

If I change however the order of calls within main then something mysterious happens:

main :: forall a. Int -> Aff (ajax :: AJAX, console :: CONSOLE| a) Int
main state = do
    a <- liftAff access
    s <- liftEff $ staging state
    return s

the function staging is now being called twice! Wut?

Can anybody explain this?

Thanks for your help

1
What happens if you remove the call to liftAff in main? I don't think it's necessary. (Not to excuse this behaviour, which does certainly seem wrong; I'm just trying to diagnose)hdgarrood
no change. The only thing that is sort of "exotic" is that I am using github.com/sectore/purescript-webpack-vanilla-hmr. Nevertheless it should behave consistently (either always 2 times or always once but not different on different orders)robkuz
Agreed. Can you run the above program through psc-bundle and upload it to a pastebin?hdgarrood
will need some time to extract only the barebone stuff without the D3 and Pux deps. On the weekendrobkuz
You shouldn't need to, psc-bundle should remove all the dead code.hdgarrood

1 Answers

0
votes

It's likely a case where an exception is being thrown rather than handled by the error function in an Aff instance. This leads to duplicate calls of the success function when used in conjunction with attempt.

module Main where

import Prelude
import Data.Either (Either(..))
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Console (log)
import Control.Monad.Eff.Exception (Error, EXCEPTION, throwException, error)
import Control.Monad.Aff (Aff, makeAff, liftEff', launchAff, attempt)

raise = throwException <<< error


myAff :: forall e. Aff e String
myAff = _unsafeInterleaveAff $ makeAff doIt
  where
    doIt _ success = do
      log "operation"
      raise "it's dead jim" 
      success "done"

main = do
  launchAff $ do
    liftEff' $ log "start"
    myAff

foreign import _unsafeInterleaveAff :: forall e1 e2 a. Aff e1 a -> Aff e2 a

This code leads to the doIt being called twice, but when the Aff calls are reversed will not.

Additional:

Though this functionality does seem a little strange. Maybe replace attempt with some more like this?

exports._attempt = function (Left, Right, aff) {
  return function(success, error) {
    var affCompleted = false;
    try {
      return aff(function(v) {
        affCompleted = true
        success(Right(v));
      }, function(e) {
        affCompleted = true
        success(Left(e));
      });
    } catch (err) {
      if (affCompleted) {
        throw err;
      } else {
        success(Left(err));
      }
    }
  };
}