Scala voor backend-engineering en functioneel programmeren · Les

Newtypes

Typesafe wrappers

Les 3 van 413 stappen

Newtypes is een gratis Scala voor backend-engineering en functioneel programmeren-les op CoddyKit. Dit is les 3 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject Scala voor backend-engineering en functioneel programmeren. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus Scala voor backend-engineering en functioneel programmeren bevat in totaal 4 lessen.

Typesafe wrappers

Een newtype is een afzonderlijk type dat één onderliggende waarde omhult. Het voorkomt dat je waarden verwisselt die dezelfde primitieve representatie hebben, zoals een UserId en een ProductId die beide Ints zijn.

case class UserId(value: Int)
case class ProductId(value: Int)

object Main:
  def main(args: Array[String]): Unit =
    val u = UserId(1)
    val p = ProductId(1)
    println(u.value == p.value) // values equal, types differ

Het probleem van primitieve fixatie

Primitieve fixatie betekent dat je String en Int te veel gebruikt voor domeinconcepten. Daardoor is onzinnigheid mogelijk, zoals een e-mailadres doorgeven waar een naam hoort. Newtypes lossen dit op door elk concept een eigen type te geven.

case class Email(value: String)
case class City(value: String)

object Main:
  def greet(c: City): String = s"Welcome to ${c.value}"

  def main(args: Array[String]): Unit =
    println(greet(City("Berlin")))

Newtypes met case class

De eenvoudigste newtype is een case class met één veld. Deze biedt gelijkheid, een leesbare toString en pattern matching zonder extra werk. De prijs is één objecttoewijzing per waarde.

case class OrderId(value: String)

object Main:
  def main(args: Array[String]): Unit =
    val id = OrderId("ORD-42")
    println(id)
    println(id.value)

Newtypes met opaque types

Gebruik voor nul toewijzingen een opaque type als newtype. Tijdens runtime gedraagt het zich als het onderliggende type, maar tijdens het compileren is het ervan onderscheiden.

object Ids:
  opaque type UserId = Int
  def apply(i: Int): UserId = i
  extension (u: UserId) def value: Int = u

object Main:
  def main(args: Array[String]): Unit =
    val u = Ids(7)
    println(u.value)

Gedrag toevoegen met extensies

Geef een newtype bewerkingen via extension-methoden, waarbij je alleen beschikbaar maakt wat bij het domeinconcept past.

object Money:
  opaque type Cents = Long
  def apply(n: Long): Cents = n
  extension (c: Cents)
    def +(o: Cents): Cents = c + o
    def toDollars: Double = c / 100.0

object Main:
  def main(args: Array[String]): Unit =
    val total = Money(250) + Money(750)
    println(total.toDollars)

Newtypes in functiesignaturen

Newtypes maken signaturen zelfdocumenterend en detecteren fouten in de volgorde van argumenten tijdens het compileren. Je kunt niet per ongeluk twee parameters van verschillende newtypes verwisselen.

case class Width(value: Int)
case class Height(value: Int)

object Main:
  def area(w: Width, h: Height): Int = w.value * h.value

  def main(args: Array[String]): Unit =
    println(area(Width(4), Height(5)))

Newtypes en verzamelingen

Een Map met een newtype als sleutel is duidelijker en veiliger dan een map met een onbewerkte Int als sleutel. Het type voorkomt dat je de verkeerde soort sleutel gebruikt.

case class UserId(value: Int)

object Main:
  def main(args: Array[String]): Unit =
    val names = Map(UserId(1) -> "Ada", UserId(2) -> "Bob")
    println(names(UserId(2)))

Gevalideerde newtypes

Combineer een newtype met een slimme constructor, zodat de wrapper ook invarianten afdwingt. Een NonEmptyString kan hier nooit leeg zijn.

case class NonEmptyString private (value: String)
object NonEmptyString:
  def of(s: String): Option[NonEmptyString] =
    if s.nonEmpty then Some(NonEmptyString(s)) else None

object Main:
  def main(args: Array[String]): Unit =
    println(NonEmptyString.of("hi"))
    println(NonEmptyString.of(""))

Een representatie kiezen

Kies op basis van je behoeften.

  • case class: het eenvoudigst, geschikt voor pattern matching, maar maakt een object aan.
  • opaque type: zonder kosten, zonder boxing, ideaal voor kritieke codepaden en grote verzamelingen.
object Temp:
  opaque type Kelvin = Double
  def apply(d: Double): Kelvin = d
  extension (k: Kelvin) def value: Double = k

object Main:
  def main(args: Array[String]): Unit =
    val readings = List(Temp(300.0), Temp(310.5))
    println(readings.map(_.value).sum)

Onbedoelde conversies voorkomen

Twee newtypes op basis van hetzelfde type worden niet impliciet naar elkaar geconverteerd. Om van het ene naar het andere te gaan, schrijf je een expliciete functie die de bedoeling documenteert.

case class Meters(value: Double)
case class Feet(value: Double)

object Main:
  def toFeet(m: Meters): Feet = Feet(m.value * 3.281)

  def main(args: Array[String]): Unit =
    println(toFeet(Meters(2.0)))

Wanneer gebruik je newtypes

Gebruik newtypes zodra een primitief type een betekenis in het domein heeft.

  • Identificatiegegevens, eenheden en opgemaakte tekenreeksen.
  • Gebruik een case class voor eenvoud en een opaque type voor prestaties.
  • Voeg een slimme constructor toe wanneer er invarianten zijn.
object Domain:
  opaque type Sku = String
  def of(s: String): Option[Sku] =
    if s.startsWith("SKU-") then Some(s) else None
  extension (k: Sku) def raw: String = k

object Main:
  def main(args: Array[String]): Unit =
    println(Domain.of("SKU-9").map(_.raw))

Korte controle

Test je begrip van newtypes.

Samenvatting

Je hebt newtypes geleerd.

  • Newtypes omhullen één onderliggende waarde in een afzonderlijk type.
  • Ze verhelpen primitieve fixatie en voorkomen verwisselingen van argumenten.
  • Gebruik case class voor eenvoud en opaque type voor nul kosten.
  • Voeg een slimme constructor toe voor gevalideerde wrappers.
  • Conversies tussen newtypes zijn altijd expliciet.
object Ids:
  opaque type AccountId = Long
  def apply(n: Long): AccountId = n
  extension (a: AccountId) def value: Long = a

object Main:
  def main(args: Array[String]): Unit =
    val acc = Ids(1001L)
    println(acc.value)
Gratis beginnen

Leer Scala met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
39
Lessen
143

Veelgestelde vragen

Is de les “Newtypes” gratis?

Ja — de volledige tekst van “Newtypes” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus Scala voor backend-engineering en functioneel programmeren wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus Scala voor backend-engineering en functioneel programmeren bevat in totaal 4 lessen.

Wat leer ik in “Newtypes”?

Typesafe wrappers Je oefent met Scala voor backend-engineering en functioneel programmeren door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met Scala voor backend-engineering en functioneel programmeren te beginnen?

Ervaring vooraf is niet nodig. Scala voor backend-engineering en functioneel programmeren op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 3 van 4.

Hoe lang duurt de les “Newtypes”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over Scala voor backend-engineering en functioneel programmeren?

Ja. Elke les over Scala voor backend-engineering en functioneel programmeren bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. Modelleren met ADT's
  2. Slimme constructors
  3. Newtypes
  4. Domeinen combineren
← Terug naar Scala voor backend-engineering en functioneel programmeren