def parsePort(s: String): Int = s.toInt parsePort("Ups!") def startSocket(port: Int): Unit = { val serverSocket = new ServerSocket(port) // ... } startSocket(parsePort("-1")) // Ups... Dies ist nur ein Beispiel von vielen.
ugbaren Typen durch weitere Beschr¨ ankungen. type PortNumber = Int Refined Interval.Closed[W.‘1‘.T, W.‘65535‘.T] def startSocket(p: PortNumber): Unit = { val serverSocket = new ServerSocket(p) // ... } startSocket(-1) // Compiliert nicht!
desto einfacher ist die Implementierung. final case class Service(host: String, port: Int) def hashName(name: String) versus final case class Service(host: String Refined IPv4, port: PortNumber) def hashName(name: String Refined NonEmpty)
System • Produktdaten von extern • weitgehend kompatibel • Altsystem unterst¨ utzt nur Artikelnummern gr¨ oßer final case class ArtNo(n: Int) { require(n > 4096, "Illegal article number!") } Das ist beschissen! Tut das nicht!
final case class ArtNo(n: Int) object ArtNo { def apply(n: Int): Option[ArtNo] = if (n > 4096) Option(new ArtNo(n)) else None } Aber wir k¨ onnen das hier haben... :-) type ArtNo = Int Refined Greater[4096]
Erstellen von diversen Formaten • heutzutage zumeist JSON • auch hier erh¨ ohte Sicherheit durch strengere Typisierung der Modelle • Integration von refined mit Circe (JSON Bibiliothek) • Decoder und Encoder frei Haus
eigentlich? • sehr oft Eingabewerte (erlaubte Wertebereich) "Creating a user" when { "name is empty" must { "throw an exception" in { the [IllegalArgumentException] thrownBy createUser(name = "", age = 18) must have message "Name must be present!" } } }