النمذجة باستخدام ADTs
اجعل الحالات غير القانونية غير قابلة للتمثيل
النمذجة باستخدام ADTs درس مجاني في Scala for Backend Engineering & Functional Programming على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Scala for Backend Engineering & Functional Programming، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Scala for Backend Engineering & Functional Programming 4 دروس في المجموع.
بعض أجزاء هذا الدرس لم تُترجم بعد وتظهر باللغة الإنجليزية.
Algebraic Data Types
Algebraic data types (ADTs) are the foundation of functional domain modeling. They combine product types (AND) and sum types (OR) to describe data precisely.
- Product: a record with several fields.
- Sum: a choice among several variants.
case class Point(x: Int, y: Int) // product type
object Main:
def main(args: Array[String]): Unit =
println(Point(1, 2))Product Types
A product type bundles values together. In Scala a case class is a product: an instance holds all its fields at once.
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)Sum Types
A sum type is a value that is exactly one of several variants. Scala 3 enums express this directly.
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)Make Illegal States Unrepresentable
The core principle: design types so that invalid data cannot even be constructed. If the type system forbids a bad state, you never need a runtime check for it.
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)Bad Design vs Good Design
A flat record with nullable fields invites illegal states. Modeling each case as a variant removes them.
- Bad:
case class Conn(connected: Boolean, sessionId: String)allows connected=false with a sessionId. - Good: a sum type ties the field to the right state.
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)Combining Products and Sums
Real models nest products inside sums and vice versa. Each variant can carry its own product of fields.
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)Pattern Matching ADTs
You consume ADTs with pattern matching. Because the type is closed, the compiler warns if you miss a 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)))Recursive ADTs
ADTs can refer to themselves, modeling trees, lists, and expressions naturally.
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))Optionality with Option
Use Option instead of null to model a possibly-absent field. The type makes the absence explicit and forces handling.
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)"))Modeling Quantities Precisely
Replace primitive obsession. Instead of raw Ints and Strings, wrap meaningful quantities so the types document intent and prevent mixups.
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)Why ADTs Matter
ADTs give you correctness and clarity.
- Invalid states cannot be built.
- Exhaustive matching catches missed cases at compile time.
- The shape of the data documents the domain.
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)Quick Check
Test your understanding of ADT-based modeling.
Recap
You learned domain modeling with ADTs.
- Product types (
case class) combine fields with AND. - Sum types (
enum) offer a choice with OR. - Nest them to model rich domains and recursive structures.
- Use
Optioninstead of null. - Design so illegal states cannot be represented.
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")))الأسئلة الشائعة
هل درس «النمذجة باستخدام ADTs» مجاني؟
نعم — نص درس «النمذجة باستخدام ADTs» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Scala for Backend Engineering & Functional Programming، انتقل إلى CoddyKit PRO. تتضمن دورة Scala for Backend Engineering & Functional Programming 4 دروس في المجموع.
ماذا ستتعلم في «النمذجة باستخدام ADTs»؟
اجعل الحالات غير القانونية غير قابلة للتمثيل تتمرن على Scala for Backend Engineering & Functional Programming مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Scala for Backend Engineering & Functional Programming؟
لا تُشترط خبرة سابقة. Scala for Backend Engineering & Functional Programming على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.
كم من الوقت يستغرق درس «النمذجة باستخدام ADTs»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Scala for Backend Engineering & Functional Programming هذا؟
نعم. كل درس في Scala for Backend Engineering & Functional Programming يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- النمذجة باستخدام ADTs
- المنشئات الذكية
- الأنواع الجديدة
- تركيب المجالات