2016-04-08 15 views
0

Ich habe ein paar Funktionen mit einem gleichen Codierungsmuster:Funktion als Parameter

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") 
    } 
} 

Um meinen Code weniger wiederholt zu machen, habe ich eine neue private Funktion als

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") 
    } 
} 

wo

schreiben
trait DomainObject { ... } 

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

Mit den obigen Änderungen kann ich updateDomainObjectValidationStatus innerhalb von updateFooValidationStatus wegen a nicht aufrufen n Fehler auf einen Parameter

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

Interessanterweise beschwert sich nicht den ersten Parameter

(Long) => Option[DomainObject] 

die

nimmt
(Long) => Option[Foo] 

Was wird ein Code-Design in der Scala sein idiomatisch Mode Machen Sie den obigen Code funktioniert?

+0

'Def' sind keine Funktionen, sie sind Methoden – pedrofurla

Antwort

4

Was ist hier passiert: updateDomainObjectValidationStatus keinen Wert mit Typ (Foo) => Option[Foo] als Argument vom Typ (DomainObject) => Option[DomainObject], weil Funktionen kontra in ihrer Argumentation Art (en) und covariant in ihrem Rückgabetyp annehmen kann sind. Was bedeutet das?

  • Wenn AB erstreckt sich dann eine Funktion RückkehrA eine Funktion B Rückkehr erstreckt (das ist, warum Compiler nicht über Ihren ersten Parameter nicht beschweren)
  • Wenn jedoch AB erstreckt, dann eine Funktion mit Argument vom Typ B erweitert eine Funktion mit Argument vom Typ A - also anders herum!

Warum macht das Sinn?

Denken Sie darüber nach: Wenn A extends B, können wir sagen "A ist ein B". Nun, "eine Funktion auf A" kann auf jedem A, aber nicht unbedingt auf jedem B funktionieren, oder? Wenn Ihre Funktion ein Int Argument hat, können Sie keinen Wert mit AnyVal Typ übergeben ... Das Gegenteil funktioniert - eine Funktion auf AnyVal kann definitiv mit einem Int aufgerufen werden.

dies zu beheben - in diesem Fall sieht aus wie der beste Ansatz updateDomainObjectValidationStatus einen Typ, die sich DomainObject geben würde:

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

Wenn Sie es die mit einem ersten Parameter des Typs (Long) => Option[Foo], rufen Der Typ T wird als Foo gewertet, und der Compiler erwartet dann wie erwartet (Foo) => Option[Foo] für den dritten.Sie erhalten sogar einen netten Bonus - der Rückgabetyp wäre dann spezifischer: Option[Foo] statt Option[DomainObject].

+0

Vielen Dank für die detaillierte Erklärung. Für die Änderung muss ich das DomainObject-Merkmal sowie seine Unterfallklassen parametrisieren. In der Eigenschaft ist eine Methodensignatur definiert. Ich muss es nach einigen Fixes ausarbeiten. – TeeKai