Почему мой эффект вызывается дважды?

У меня есть следующая программа, написанная с Effs и Affs. Который работает, как и ожидалось. То есть он выводит заданное Int и выполняет асинхронные вычисления.

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

Однако если я изменю порядок вызовов в main, произойдет нечто загадочное:

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

функция staging теперь вызывается дважды! Что?

Кто-нибудь может это объяснить?

Спасибо за вашу помощь


person robkuz    schedule 29.04.2016    source источник
comment
Что произойдет, если вы удалите вызов liftAff в main? Я не думаю, что это необходимо. (Не для оправдания такого поведения, которое, безусловно, кажется неправильным; я просто пытаюсь поставить диагноз)   -  person hdgarrood    schedule 29.04.2016
comment
без изменений. Единственное, что немного экзотично, это то, что я использую github.com/sectore/purescript -webpack-vanilla-hmr. Тем не менее, он должен вести себя последовательно (либо всегда 2 раза, либо всегда один раз, но не по-разному в разных порядках)   -  person robkuz    schedule 29.04.2016
comment
Согласовано. Можете ли вы запустить вышеуказанную программу через psc-bundle и загрузить ее на pastebin?   -  person hdgarrood    schedule 29.04.2016
comment
потребуется некоторое время, чтобы извлечь только barebone-материал без D3 и Pux. На выходных   -  person robkuz    schedule 29.04.2016
comment
Вам не нужно, psc-bundle должен удалить весь мертвый код.   -  person hdgarrood    schedule 29.04.2016
comment
Почему main имеет такой тип, а не Eff _ _?   -  person Phil Freeman    schedule 29.04.2016
comment
@PhilFreeman, потому что я получаю main :: forall a. Int -> Eff (ajax :: AJAX, console :: CONSOLE| a) Int ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ No type class instance was found for Control.Monad.Aff.Class.MonadAff ( ajax :: AJAX | _0 ) (Eff ( ajax :: AJAX, console :: CONSOLE | a0 ) ) The instance head contains unknown type variables. Consider adding a type annotation. in value declaration main   -  person robkuz    schedule 30.04.2016
comment
Итак, вам нужно использовать что-то, чтобы превратить Aff обратно в Eff, например launchAff.   -  person Phil Freeman    schedule 30.04.2016
comment
@PhilFreeman, это не столько обходной путь. А просто вопрос ПОЧЕМУ? почему это называется дважды. Это странно   -  person robkuz    schedule 01.05.2016
comment
Нет, я говорю, что вы должны ожидать непредсказуемых результатов, если тип main не соответствует ожидаемому Pulp или psc-bundle. Если вы не используете main каким-то другим способом.   -  person Phil Freeman    schedule 02.05.2016


Ответы (1)


Скорее всего, это случай, когда исключение генерируется, а не обрабатывается функцией ошибок в экземпляре Aff. Это приводит к дублированию вызовов функции успеха при использовании в сочетании с 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

Этот код приводит к тому, что doIt вызывается дважды, но когда вызовы Aff меняются местами, этого не происходит.

Дополнительный:

Хотя эта функциональность кажется немного странной. Может быть, заменить attempt на что-то подобное?

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));
      }
    }
  };
}
person user3703967    schedule 12.06.2016