Scala for backendutvikling og funksjonell programmering · leksjon

Modellering med ADT-er

Gjør ulovlige tilstander umulige å representere

Leksjon 1 av 413 trinn

Modellering med ADT-er er en gratis leksjon i Scala for backendutvikling og funksjonell programmering på CoddyKit. Dette er leksjon 1 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Scala for backendutvikling og funksjonell programmering, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Scala for backendutvikling og funksjonell programmering inneholder totalt 4 leksjoner.

Algebraiske datatyper

Algebraiske datatyper (ADT-er) er grunnlaget for funksjonell domenemodellering. De kombinerer produkt- (AND) og sumtyper (OR) for å beskrive data presist.

  • Produkt: en post med flere felt.
  • Sum: et valg mellom flere varianter.
case class Point(x: Int, y: Int) // product type

object Main:
  def main(args: Array[String]): Unit =
    println(Point(1, 2))

Produktstyper

En produkttype samler verdier. I Scala er en case class et produkt: en instans inneholder alle feltene sine samtidig.

case class User(name: String, age: Int)

object Main:
  def main(args: Array[String]): Unit =
    val u = User("Ada", 36)
    println(u.name)
    println(u.age)

Sumtyper

En sumtype er en verdi som er nøyaktig én av flere varianter. Enum-er i Scala 3 uttrykker dette direkte.

enum PaymentMethod:
  case Cash
  case Card(number: String)
  case Crypto(wallet: String)

object Main:
  def main(args: Array[String]): Unit =
    val p: PaymentMethod = PaymentMethod.Card("1234")
    println(p)

Gjør ugyldige tilstander umulige å representere

Kjerneprinsippet er å utforme typer slik at ugyldige data ikke engang kan konstrueres. Hvis typesystemet forbyr en ugyldig tilstand, trenger De aldri en kontroll av den under kjøring.

enum Connection:
  case Disconnected
  case Connected(sessionId: String)

object Main:
  def main(args: Array[String]): Unit =
    // No way to have a sessionId while Disconnected
    val c: Connection = Connection.Connected("abc")
    println(c)

Dårlig design kontra god design

En flat post med nullable felt legger til rette for ugyldige tilstander. Ved å modellere hvert tilfelle som en variant fjernes de.

  • Dårlig: case class Conn(connected: Boolean, sessionId: String) tillater connected=false med en sessionId.
  • Godt: en sumtype knytter feltet til riktig tilstand.
enum Door:
  case Open
  case Closed
  case Locked(key: String)

object Main:
  def main(args: Array[String]): Unit =
    val d: Door = Door.Locked("k1")
    println(d)

Kombinere produkter og summer

Reelle modeller nøster produkter inn i summer og omvendt. Hver variant kan inneholde sitt eget produkt av felt.

case class Address(city: String, zip: String)

enum Contact:
  case Email(value: String)
  case Postal(address: Address)

object Main:
  def main(args: Array[String]): Unit =
    val c: Contact = Contact.Postal(Address("Paris", "75001"))
    println(c)

Mønstermatching på ADT-er

De bruker mønstermatching til å behandle ADT-er. Fordi typen er lukket, varsler kompilatoren hvis De utelater en variant.

enum Shape:
  case Circle(r: Double)
  case Rect(w: Double, h: Double)

object Main:
  def area(s: Shape): Double = s match
    case Shape.Circle(r)  => 3.14159 * r * r
    case Shape.Rect(w, h) => w * h

  def main(args: Array[String]): Unit =
    println(area(Shape.Rect(2, 3)))

Rekursive ADT-er

ADT-er kan referere til seg selv og modellere trær, lister og uttrykk på en naturlig måte.

enum Expr:
  case Num(value: Int)
  case Add(left: Expr, right: Expr)

object Main:
  def eval(e: Expr): Int = e match
    case Expr.Num(v)    => v
    case Expr.Add(l, r) => eval(l) + eval(r)

  def main(args: Array[String]): Unit =
    val e = Expr.Add(Expr.Num(2), Expr.Num(3))
    println(eval(e))

Valgfrihet med Option

Bruk Option i stedet for null for å modellere et felt som kanskje ikke finnes. Typen gjør fraværet eksplisitt og tvinger frem håndtering av det.

case class Profile(name: String, nickname: Option[String])

object Main:
  def main(args: Array[String]): Unit =
    val p = Profile("Grace", None)
    println(p.nickname.getOrElse("(none)"))

Modellere mengder presist

Unngå primitiv fiksering. I stedet for rå Int- og String-verdier kan De pakke inn meningsfulle mengder, slik at typene dokumenterer hensikten og forhindrer forvekslinger.

case class Quantity(value: Int)
case class Price(cents: Long)
case class LineItem(qty: Quantity, price: Price)

object Main:
  def main(args: Array[String]): Unit =
    val item = LineItem(Quantity(3), Price(500))
    println(item)

Hvorfor ADT-er er viktige

ADT-er gir Dem korrekthet og tydelighet.

  • Ugyldige tilstander kan ikke bygges.
  • Uttømmende matching oppdager manglende tilfeller ved kompilering.
  • Datastrukturen dokumenterer domenet.
enum OrderStatus:
  case Pending
  case Shipped(tracking: String)
  case Delivered(at: Long)

object Main:
  def main(args: Array[String]): Unit =
    val s: OrderStatus = OrderStatus.Shipped("TRK1")
    println(s)

Kort kontroll

Test forståelsen Deres av ADT-basert modellering.

Oppsummering

De har lært domenemodellering med ADT-er.

  • Produkttyper (case class) kombinerer felt med AND.
  • Sumtyper (enum) tilbyr et valg med OR.
  • Nest dem for å modellere rike domener og rekursive strukturer.
  • Bruk Option i stedet for null.
  • Utform modellen slik at ugyldige tilstander ikke kan representeres.
enum Event:
  case Created(id: Int)
  case Deleted(id: Int, reason: String)

object Main:
  def describe(e: Event): String = e match
    case Event.Created(id)         => s"created $id"
    case Event.Deleted(id, reason) => s"deleted $id: $reason"

  def main(args: Array[String]): Unit =
    println(describe(Event.Deleted(1, "spam")))
Gratis å komme i gang

Lær deg Scala med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
39
Leksjoner
143

Ofte stilte spørsmål

Er leksjonen «Modellering med ADT-er» gratis?

Ja – hele teksten i «Modellering med ADT-er» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av Scala for backendutvikling og funksjonell programmering-kurset, kan du oppgradere til CoddyKit PRO. Kurset i Scala for backendutvikling og funksjonell programmering inneholder totalt 4 leksjoner.

Hva lærer jeg i «Modellering med ADT-er»?

Gjør ulovlige tilstander umulige å representere Du øver på Scala for backendutvikling og funksjonell programmering med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med Scala for backendutvikling og funksjonell programmering?

Ingen tidligere erfaring er nødvendig. Scala for backendutvikling og funksjonell programmering på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 1 av 4.

Hvor lang tid tar leksjonen «Modellering med ADT-er»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne Scala for backendutvikling og funksjonell programmering-leksjonen?

Ja. Alle Scala for backendutvikling og funksjonell programmering-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Modellering med ADT-er
  2. Smarte konstruktører
  3. Newtypes
  4. Sammensetting av domener
← Tilbake til Scala for backendutvikling og funksjonell programmering