Обнаружено несоответствие типов Объект, требуемый Future[Customer] на плоской карте

У меня есть приведенный ниже код, и в моей функции findOrCreate() я получаю сообщение об ошибке Type mismatch found Unit, required Future[Customer]. Функция customerByPhone(), которая вызывается внутри findOrCreate(), также содержит вызовы, ожидающие фьючерсы, поэтому я использую толстую карту. Я не знаю, почему в результате плоской карты получается Unit. Что я делаю не так?

override def findOrCreate(phoneNumber: String, creationReason: String): Future[AvroCustomer] =  {

  //query for customer in db
  val avroCustomer: Future[AvroCustomer] = customerByPhone(phoneNumber).flatMap(_ => createUserAndEvent(phoneNumber, creationReason, 1.0))

}

override def customerByPhone(phoneNumber: String): Future[AvroCustomer] = {
  val query = Schema.Customers.byPhoneNumber(phoneNumber)
  val dbAction: DBIO[Option[Schema.Customer]] = query.result.headOption

  db.run(dbAction)
    .map(_.map(AvroConverters.toAvroCustomer).orNull)
}

private def createUserAndEvent(phoneNumber: String, creationReason: String, version: Double): Future[AvroCustomer] = {

  val query = Schema.Customers.byPhoneNumber(phoneNumber)
  val dbAction: DBIO[Option[Schema.Customer]] = query.result.headOption

  val data: JsValue = Json.obj(
    "phone_number" -> phoneNumber,
    "agent_number" -> "placeholder for agent number",
    "creation_reason" -> creationReason
  )
  //empty for now
  val metadata: JsValue = Json.obj()

  //creates user
  val avroCustomer: Future[AvroCustomer] = db.run(dbAction).map(_.map(AvroConverters.toAvroCustomer).orNull)
  avroCustomer.onComplete({
    case Success(null) => {

    }

    //creates event
    case Success(customer) => {
      val uuid: UUID = UUID.fromString(customer.id)
      //create event
      val event: Future[CustomerEvent] = db.run(Schema.CustomerEvents.create(
        uuid,
        "customer_creation",
        version,
        data,
        metadata)
      ).map(AvroConverters.toAvroEvent)
    }
    case Failure(exception) => {

    }
  })

  Future.successful(new AvroCustomer)
}

person Rafa    schedule 11.03.2017    source источник
comment
Оператор val возвращает Unit.   -  person Reactormonk    schedule 12.03.2017
comment
@Reactormonk, насколько мне известно, val просто делает переменную неизменной, но вы можете вернуть любое значение?   -  person Rafa    schedule 12.03.2017
comment
val foo: Int = { val bar = 32 } выдаст ошибку компиляции.   -  person Reactormonk    schedule 12.03.2017
comment
val создает неизменяемую переменную с указанным значением, но не возвращает это значение, как в таком языке, как C.   -  person jwvh    schedule 12.03.2017


Ответы (1)


Хотя Reactormonk в основном ответил на это в комментариях, я собираюсь написать ответ с некоторыми подробностями. Его комментарий о том, что оператор val производит Unit, в основе своей верен, но я надеюсь, что некоторые уточнения прояснят ситуацию.

Ключевой элемент, который я вижу, это то, что val является объявлением. Объявления в Scala — это операторы, которые не производят полезных значений. Из-за функциональной природы Scala они производят значение, но это Unit, а поскольку существует только один экземпляр Unit, оно не несет никакого смысла.

Причина, по которой программисты, плохо знакомые со Scala, часто испытывают искушение сделать что-то подобное, заключается в том, что они не думают о блоках кода как о операторах и часто используют return в других языках. Итак, давайте рассмотрим здесь упрощенную функцию.

def foo(i: Int): Int = {
  42 * i
}

Я включаю блок кода, так как думаю, что это ключ к этой ошибке, хотя здесь он действительно не нужен. Значение блока кода — это просто значение последнего выражения в блоке кода. Вот почему нам не нужно указывать return, но большинству программистов, привыкших к return, немного не нравится это неприкрытое выражение в конце блока. Вот почему заманчиво добавить объявление val.

def foo(i: Int): Int = {
  val result = 42 * i // Error: type mismatch.
}

Конечно, как уже упоминалось, но val приводит к тому, что Unit делает это неверным. Вы можете добавить дополнительную строку только с result, и это скомпилируется, но это слишком многословно и неидиоматично.

Scala поддерживает использование return для выхода из метода/функции и возврата определенного значения, хотя это обычно не одобряется. Таким образом, следующий код работает.

def foo(i: Int): Int = {
  return 42 * i
}

Хотя вы не должны использовать return в коде Scala, я чувствую, что представление о том, что оно там, может помочь понять, что здесь не так. Если вы вставите return перед val, вы получите следующий код.

def foo(i: Int): Int = {
  return val result = 42 * i // Error: type mismatch.
}

По крайней мере, для меня этот код явно неверен. val — это объявление, и поэтому оно просто не работает с return. Требуется некоторое время, чтобы привыкнуть к функциональному стилю блоков как выражений. Пока вы не дойдете до этого момента, может помочь просто вести себя так, как будто в конце методов есть return, не помещая его туда на самом деле.

Стоит отметить, что в комментариях jwvh утверждает, что подобное объявление в C вернет значение. Это неверно. Присваивания в большинстве языков семейства C возвращают значение, которое было присвоено, поэтому a = 5 возвращает значение 5, а объявления — нет, поэтому int a = 5; ничего не возвращает и не может использоваться как выражение.

person Mark Lewis    schedule 13.03.2017