Составление фьючерсов без сохранения состояния с фьючерсами с побочными эффектами в Scala

При составлении фьючерсов со структурой for-yield, некоторые с побочными эффектами, некоторые без, я ввел условие гонки, потому что будущее, зависящее от побочного эффекта, не принимает результат этого побочного эффекта future в качестве аргумента.

Короче:

future b читает значение, которое изменено побочным эффектом от future a, но future a явно не зависит от результата future < strong> b и, следовательно, может произойти до того, как b закончит чтение.

Чтобы решить эту проблему, мой коллега представил фиктивную функцию, принимающую в качестве аргумента результат b и просто отбрасывающую его. Это было сделано для того, чтобы зависимость была явной.

Фактический код здесь:

  val futureConnection:Future[(Either[String, (Connection)],Boolean)] =
    for {
      scannerUser <- scanner.orFail("Scanning user not found")
      scannedUser <- futureScannedUser.orFail("Scanned user not found")
      existsInAnyDirection <- connections.existsInAnyDirection(scannerUser, scannedUser)
      connection <- connections.createConnection(scannerUser, scannedUser, form.magicWord, existsInAnyDirection)
    } yield {
      (connection, existsInAnyDirection)
    }

В этом случае будущее b равно

connections.existsInAnyDirection(scannerUser, scannedUser)

а будущее a с фиктивным параметром равно

connections.createConnection(scannerUser, scannedUser, form.magicWord, existsInAnyDirection)

Обратите внимание, что параметр existsInAnyDirection никогда не используется внутри createConnection. Это эффективно создает граф зависимостей, который не может быть инициирован createConnection до завершения existsInAnyDirection.

Теперь вопрос:

Есть ли более разумный способ сделать зависимость явной?


Бонусная информация

Мои собственные исследования говорят мне, что Scala Futures просто не очень хорошо справляется с побочными эффектами. Методы признака Future, которые работают с побочными эффектами, возвращаются Unit, хотя вполне могут быть результаты для чтения из операции побочного эффекта, то есть коды ошибок, сгенерированные идентификаторы, на самом деле любая другая метаинформация.


person Felix    schedule 26.01.2016    source источник
comment
Я не понимал, почему flatMap не работает. Существует важное различие в том, когда вы создаете Future - внутри flatMap или снаружи - это повлияет на время начала Future. Возможно, это та деталь, которую вы упустили, и поэтому вы получаете состояние гонки. val f = Future { 5 }; val h = for { x <- f ... не то же самое, что val h = for { x <- Future { 5 } ... - первый запускает Future раньше, чем второй.   -  person yǝsʞǝla    schedule 26.01.2016
comment
Я имею в виду, что если scanner - это val, тогда ваш Future уже запущен до того, как вы вошли в for понимание. Если это def, значит, нет. Похоже, вам нужно сделать свой futureScannedUser a def или функцию () => Future[?]   -  person yǝsʞǝla    schedule 26.01.2016
comment
Оказывается, исправление, внесенное моим коллегой, переупорядочивало выражение for yield, а также вводило фиктивный параметр. Фактическим исправлением был переупорядочение, поэтому мой вопрос был некорректным.   -  person Felix    schedule 27.01.2016


Ответы (1)


Future обрабатывает побочный эффект отложенных вычислений, например A => Future[B].

Вы пытались смешать несколько разных побочных эффектов, но составили только один из них Future[_].

Попробуйте выбрать второй контейнер, это может быть продукт или состояние, в зависимости от вашего побочного эффекта, и подумайте о способе составления побочных эффектов (возможно, вам понадобятся мод и преобразователи). И после того, как ваш код может выглядеть (в простейших случаях):

for {
  scannerUser <- scanner.orFail("Scanning ...")
  (scannedUser, magicWord) <- futureScannedUser.orFail("Scanned ...")      
  connection <- connections.createConnection(scannerUser, scannedUser, magicWord)
} yield {
  (connection, existsInAnyDirection)
}

// OR

for {
  (scannerUser, state) <- scanner.orFail("Scanning ...")
  (scannedUser, nextState) <- futureScannedUser(state).orFail("Scanned ...")      
  connection <- connections.createConnection(scannerUser, scannedUser, nextState)
} yield {
  (connection, existsInAnyDirection)
}
person Yuriy    schedule 26.01.2016