2 Stimmen

Defensive Funktionen, Invarianten, minimale Geräusche

Ich mag die Verwendung von require, assert, assume und das Dokumentieren von Vorbedingungen und Nachbedingungen einer bestimmten Funktion, aber ich habe auch angefangen, hauptsächlich scalaz.Validation zu verwenden, um Prüfungen durchzuführen, die früher require gemacht hat. Aber in letzter Zeit interessiere ich mich am meisten für die Verwendung von PartialFunction, möglicherweise mit Validation, um eine Vorbedingung zu dokumentieren. Etwas wie:

case class Summary(user: String, at: DateTime, ....)

val restricted_add: PartialFunction[(Summary, Summary), Summary] = {
  case (s1: Summary, s2: Summary) if s1.user != s2.user && s1.at == s2.at =>    

}

mit einer Hilfsfunktion zum Erfassen und Umwandeln eines Matchfehlers in ein Validierungsfehler (z.B. restricted_add.lift(_).toSucess("Kann nicht hinzugefügt werden.")). Irgendwie scheint die obige Fallklausel weniger laut als ein For mit scalaz.Validation. Aber ich habe das Gefühl, gegen die Sprache zu handeln. Ich verwende nicht die natürliche Syntax von def zum Definieren von Parametern, jeder Client einer solchen Funktion müsste eine Hilfsfunktion beim Aufruf verwenden, ich würde Ausnahmen erfassen und in Validierungen umwandeln, anstatt nur mit Validierungen oder nur mit Ausnahmen zu arbeiten.

Was scheint der am wenigsten laute, funktionalste Weg zu sein, diese Vorbedingungen durch Code zu dokumentieren? Validation zurückgebende Funktionen, require-Anweisungen, PartialFunction, andere...?

1voto

fresskoma Punkte 24869

Wie bereits von @RandallSchulz erwähnt, dient die implizite Dokumentation von Vorbedingungen im Code nur für Ihre privaten/internen Methoden, niemals für Ihre öffentliche API. Wenn Sie öffentliche Methoden schreiben, sollten Sie auch die Vorbedingungen in der Methodendokumentation und vielleicht den Anwendungsbeispielen dokumentieren, wenn sie gegen Intuition sind.


Abgesehen davon sind meiner Meinung nach der wenigst lärmige und der funktionalste Weg in Scala nicht derselbe. Dies liegt daran, dass require nicht als funktional betrachtet werden kann, da es eine Ausnahme wirft, wenn die Bedingung nicht erfüllt ist, was irgendwo abgefangen werden würde, die Kontrollfluss ändern und somit ein Seiteneffekt wäre.

Nichtsdestotrotz denke ich, dass require bei weitem der wenigst lärmige Weg ist, Vorbedingungen anzugeben:

def non_func(x:Int, y:Int):Int = {
    require(x >= 0 && y >= 0)
    x + y
}

Ich bin mir nicht ganz sicher über den funktionalsten Weg, Vorbedingungen zu überprüfen, aber ich denke, dass die Verwendung von Pattern Matching und das Zurückgeben eines Option als funktional angesehen werden könnte, jedoch lärmiger als require:

def func(x:Int, y:Int):Option[Int] = (x,y) match {
    case (x, y) if x >= 0 && y >= 0 => Some(x + y)
    case _ => None
}

Der Vorteil der Verwendung eines Option ist, dass Sie das shit flatMap können (scnr):

val xs = List((1,2), (0,0), (-1,0), (-1,-1), (3,4))
xs.flatMap(x => func(x._1, x._2)) # => List(3, 0, 7)

Das allein ist wahrscheinlich ein guter genug Grund, nur ein wenig mehr Lärm im Vergleich zu require hinzuzufügen, da es Ihnen ermöglicht, einen Großteil der Standard-Scala-Bibliothek präziser zu nutzen.

In Bezug auf partielle Funktionen beantworten Sie im Grunde Ihre eigene Frage, da Sie sagen, dass es umständlicher ist, sie zu verwenden und zu definieren. Ich habe jedoch keine Erfahrung mit Scalaz, also kann ich den "reinen Scala"-Ansatz nicht wirklich mit dem vergleichen, was man mit Scalaz tun könnte.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X