作为参数的功能

时间:2016-04-08 22:27:05

标签: scala

我有一些具有相同编码模式的函数:

def updateFooValidationStatus(fooId: Long, user: String, valid: Boolean): Option[Foo] = {

  val fooMaybe = fooDao.getFooById(activityId)
  fooMaybe match {
    case Some(foo) => {
      fooDao.update(foo.copy(updatedBy = Some(user),
        validationStatus = if (valid) Some(DataEntryValidationStatus.Valid) else Some(DataEntryValidationStatus.Invalid))
    )
   }
   case None =>
    throw new DomainException(s"Foo with ID: '$fooId' doesn't exist")
  }
}

为了减少重复我的代码,我编写了一个新的私有函数

private def updateDomainObjectValidationStatus(f1: Long => Option[DomainObject],
               parameter1: Long,
               f2: DomainObject => Option[DomainObject],
               paramter2: String,
               valid: Boolean ): Option[DomainObject] ={

  val somethingMaybe = f1(parameter1)
  somethingMaybe match {
    case Some(something) =>
      f2(
        something.copyMe(updatedBy = Some(paramter2),
        validationStatus = if(valid) Some(DataEntryValidationStatus.Valid) else Some(DataEntryValidationStatus.Invalid))
      )
    case None =>
      throw new DomainException(s"Object with ID: '$parameter1' doesn't exist")
  }
}

,其中

trait DomainObject { ... }

case class Foo( ... ) extends DomainObject { ... }

根据上述更改,由于一个参数上的错误,我无法在updateFooValidationStatus内调用updateDomainObjectValidationStatus

type mismatch, expected (DomainObject) => Option[DomainObject], actual (Foo) => Option[Foo]

有趣的是,它并没有抱怨第一个参数

(Long) => Option[DomainObject]

需要

(Long) => Option[Foo]

Scala惯用方式中的代码设计将使上述代码有效吗?

1 个答案:

答案 0 :(得分:4)

这里发生的事情是:updateDomainObjectValidationStatus无法接受类型为(Foo) => Option[Foo]的值作为(DomainObject) => Option[DomainObject]类型的参数,因为函数是逆变< / em>在他们的参数类型和协变的返回类型。这是什么意思?

  • 如果A扩展B,则函数返回 A会扩展一个返回B的函数(这就是为什么编译器没有&## 39;抱怨你的第一个参数)
  • 但是,如果A扩展B,那么类型为B参数的函数会扩展名为A的参数的函数 - 意思是,反过来!

为什么这有意义?

考虑一下:如果A extends B,我们可以说&#34; A是B&#34;。现在,&#34; A&#34;可以使用任何A,但不一定适用于任何B,对吧?如果你的函数有一个Int参数,你就不能传递一个AnyVal类型的值...反之亦然 - AnyVal上的一个函数绝对可以调用Int

要解决此问题 - 在这种情况下,看起来最好的方法是为updateDomainObjectValidationStatus提供扩展DomainObject类型

private def updateDomainObjectValidationStatus[T <: DomainObject](
   f1: Long => Option[T],
   parameter1: Long,
   f2: T => Option[T],
   paramter2: String,
   valid: Boolean): Option[T] = { ... } 

当您使用类型为(Long) => Option[Foo]的第一个参数调用它时,类型T被推断为Foo,然后编译器期望(Foo) => Option[Foo]为第三个参数,如预期。你甚至可以得到一个很好的奖励 - 返回类型会更具体:Option[Foo]而不是Option[DomainObject]