Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Higher Kinded Data Types

Higher Kinded Data Types

Eine kurze Einführung in Higher Kinded Data Types (Motivation und Beispiele).

Jens Grassel

March 11, 2021
Tweet

More Decks by Jens Grassel

Other Decks in Programming

Transcript

  1. Was ist ein Higher Kinded ...? Was ist eigentlich ein

    ”Higher Kinded Type”? • Ein Typ, der einen anderen enthalten kann. • A[ ] → A[B] • oder leichter verst¨ andlich List[ ] → List[Int] Aber um diese geht es hier nicht direkt.
  2. Modelle in so einer Webanwendung Product Owner: ”Wir brauchen eine

    Registrierung!” • Datenbanktabelle • internes Modell • Registrierungsformular (eigenes Modell) • Validierung • Konvertierung
  3. Registrierungsmodelle ( / ) . Datenbanktabelle → geschenkt . internes

    Modell final case class Customer(id: CustomerID, name: CustomerName, email: EMail, address: PostalAddress, phone: PhoneNumber, contact: ContactName, vat: VATNumber, flag1: BlaBlaFlag, flag2: AnotherBlaBlaFlag) . nicht alle Attribute ”nach außen” durchschleifen . eigenes Modell f¨ ur Registrierung • erlaubt automatische Formulargenerierung • flexibler
  4. Registrierungsmodelle ( / ) . Registrierungsmodell final case class RegistrationForm(email:

    EMail, pass: UserPassword, name: CustomerName, address: PostalAddress, phone: PhoneNumber, contact: ContactName, vat: VATNumber) . vermutlich sowas wie object RegistrationForm { def validate(webForm: Map[String, String]): ValidatedNel[Error, RegistrationForm] = ??? } Validierung ist schonmal nicht schlecht, aber: Parse, don’t validate!
  5. Registrierungsmodelle ( / ) Wohin f¨ uhrt uns das? .

    Registrierungsmodell (wie gehabt) . Registrierungsmodell (Formularendpunkt) final case class RegistrationFormSubmitted(email: Option[EMail], pass: Option[UserPassword], name: Option[CustomerName], ...) . Registrierungsmodell (Parser) final case class RegistrationFormParsed( email: Either[String, EMail], pass: Either[String, UserPassword], name: Either[String, CustomerName], ...) Ihr versteht, was ich meine...
  6. Ein anderer Fall... Attribute, die optional sind, aber f¨ ur

    manche Funktionen gesetzt sein m¨ ussen. • Parameterklasse final case class MyArgs(id: ProgramID, filters: List[Filter], startDate: Option[OffsetDateTime], endDate: Option[OffsetDateTime]) • Funktion A def doSomething(args: MyArgs): ??? = ??? • Funktion B def doSomethingWithDates(args: MyArgs): ??? = { val s = args.startDate.get // crash val e = args.endDate.get // and burn ... }
  7. Sie abstrahieren den Wrapper ums Attribut. • statt verschiedener Modelle,

    die logisch, aber nicht f¨ ur den Compiler ersichtlich zusammenh¨ angen • ein Modell, das Strukturen der Felder abstrahiert • weniger Boilerplate • De-Duplizierung • strukturierte Beziehungen (f¨ ur Compiler verst¨ andlich)
  8. Fall (Registrierung) Annahme: Alle Felder m¨ ussen ausgef¨ ullt und

    g¨ ultig sein. final case class RegistrationForm[T[_]](email: T[EMail], pass: T[UserPassword], name: T[CustomerName], address: T[PostalAddress], phone: T[PhoneNumber], contact: T[ContactName], vat: T[VATNumber]) in der Anwendung: RegistrationForm[Either[String, _]](...) RegistrationForm[Option](...)
  9. Fall (optionale Felder) Das gleiche Prinzip, nur halt nicht f¨

    ur alle Felder: final case class MyArgs[T[_]](id: ProgramID, filters: List[Filter], startDate: T[OffsetDateTime], endDate: T[OffsetDateTime])
  10. Und was jetzt? - oder - Wie komme ich an

    die Felder? Machen wir jetzt immer sowas wie foo.bar.get???
  11. Identity to the rescue! • aus Cats Bibliothek: type Id[A]

    = A • soll heißen RegistrationForm[Id](...) MyArgs[Id](...) • f¨ ur Funktionen def doSomethingWithDates(args: MyArgs[Id]): ??? = { val s = args.startDate // safe val e = args.endDate // safe ... }
  12. Zusammenfassung • Polymorphismus (auf HKTs) → HKDs erlaubt • Wiederverwendung

    von Datenstrukturen • Reduzierung von ”duplicate code” • weniger Boilerplate • h¨ ohere Typensicherheit (Compiler hilft)