diff --git a/slides/sources/enum-1.md b/slides/sources/enum-1.md new file mode 100644 index 0000000000000000000000000000000000000000..2a94488ec178839824beb317508b7bb62bf9428e --- /dev/null +++ b/slides/sources/enum-1.md @@ -0,0 +1,328 @@ +% Enums +% +% + +Enums: Motivation +================= + +We have seen that case classes *aggregate several values* into a single abstraction. +For instance, the `Rational` case class aggregates a numerator and a denominator. + +Conversely, how could we define an abstraction *accepting alternative values*? + +\example + +Define a `Paradigm` type that can be either `Functional` or `Imperative`. + +Enums +===== + +We can define an abstraction accepting alternative values with an \red{enum}: + +~~~ +enum Paradigm + case Functional, Imperative +~~~ + +This definition introduces: + +- A new \red{type}, named `Paradigm`. +- Two possible \red{values} for this type, `Paradigm.Functional` and + `Paradigm.Imperative`. + +Enumerate the Values of an Enumeration +====================================== + +It is possible to enumerate all the values of an enum by calling the +`values` operation on the enum companion object: + +\begin{tabular}{ll} + \verb@Paradigm.values@ \wsf Array(Functional, Imperative) \\ + \verb@val p = Paradigm.Functional@ \wsf p: Paradigm = Functional \\ + \verb@p == Paradigm.values(0)@ \wsf true +\end{tabular} + +Discriminate the Values of an Enumeration +========================================= + +You can discriminate between the values of an enum by using a \red{match} +expression: + +~~~ +def isReferentiallyTransparent(paradigm: Paradigm): Boolean = + paradigm match + case Paradigm.Functional => true + case Paradigm.Imperative => false +~~~ + +-> + +And then: + +\begin{tabular}{ll} + \verb@val p = Paradigm.Functional@ \wsf p: Paradigm = Functional \\ + \verb@isReferentiallyTransparent(p)@ \wsf true +\end{tabular} + +Match Syntax +============ + +_Pattern matching_ is a generalization of `switch` from C/Java +to class hierarchies. + +It’s expressed in Scala using the `match` keyword. + +- `match` is followed by a sequence of \red{cases}, `case value => expr`. +- Each case associates an \red{expression} `expr` with a + \red{constant} `value`. + +We will see in the next slides that pattern matching can do more +than discriminating enums. + +Enumerations Can Have Methods +============================= + +Alternatively, we can define `isReferentiallyTransparent` as a method: + +~~~ +enum Paradigm + case Functional, Imperative + + def isReferentiallyTransparent: Boolean = this match + case Functional => true + case Imperative => false +~~~ + +-> + +And then: + +\begin{tabular}{ll} + \verb@val p = Paradigm.Functional@ \wsf p: Paradigm = Functional \\ + \verb@p.isReferentiallyTransparent@ \wsf true +\end{tabular} + +Enumerations Values Can Take Parameters +======================================= + +Consider the following definition of a data type modeling students +following a lecture: + +~~~ +enum Student + case Listening + case Asking(question: String) +~~~ + +This definition introduces a `Student` type consisting of two cases, +`Listening` or `Asking`. The `Asking` case is parameterized with a +value parameter `question`. Since `Listening` is not parameterized, +it is treated as a normal enum value. + +Matching on Constructors +======================== + +Since `Asking` is not a constant, we match on it using +a _constructor pattern_: + +~~~ +student match + case Student.Listening => "Student is listening" + case Student.Asking(q) => s"Student is asking: $q" +~~~ + +A constructor pattern allows us to _extract_ the value of +the `question` parameter, in case the `student` value is +indeed of type `Asking`. + +Here, the `q` identifier is bound to the `question` parameter +of the `student` object. + +Evaluating Match Expressions +============================ + +An expression of the form +$$\btt +e\ match\ \{\ case\ p_1 => e_1\ ...\ case\ p_n => e_n\ \} +$$ +matches the value of the selector $\btt e$ with the patterns +$\btt p_1, ..., p_n$ in the order in which they are written. + +The whole match expression is rewritten to the right-hand side of the first +case where the pattern matches the selector $e$. + +References to pattern variables are replaced by the corresponding +parts in the selector. + +Forms of Patterns +================= + +Patterns are constructed from: + + - _constructors_, e.g. `Rational`, `Student.Asking`, + - _variables_, e.g. `n`, `e1`, `e2`, + - _wildcard patterns_ `_`, + - _constants_, e.g. `1`, `true`, `Paradigm.Functional`. + +Variables always begin with a _lowercase letter_. + +The same variable name can only appear _once_ in a pattern. + +Names of constants begin with a capital letter, +with the exception of the reserved words `null`, `true`, +`false`. + +What Do Patterns Match? +======================= + +- A constructor pattern $\btt C(p_1 , ..., p_n)$ matches + all the values of type $\btt C$ (or a subtype) that have been + constructed with arguments matching the patterns $\btt p_1, ..., p_n$. +- A variable pattern $\btt x$ matches any value, and + \red{binds} the name of the variable to this value. +- A constant pattern $\btt c$ matches values that are equal to + $\btt c$ (in the sense of `==`) + +Matching on Case Classes +======================== + +Constructor patterns also works with case classes: + +~~~ +def invert(x: Rational): Rational = + x match + case Rational(0, _) => sys.error("Unable to invert zero") + case Rational(n, d) => Rational(d, n) +~~~ + +Relationship Between Case Classes and Parameterized Enum Cases +============================================================== + +Case classes and parameterized enum cases are very similar. + +When should you use one or the other? + +- Parameterized enum cases should be used to define a type + that is part of a set of alternatives, +- If there are no alternatives, just use a case class. + +Enumerations Can Be Recursive +============================= + +A list of integer values can be modeled as either an empty list, +or a node containing both a number and a reference to the remainder +of the list. + +~~~ +enum List + case Empty + case Node(value: Int, next: List) +~~~ + +A list with values `1`, `2`, and `3` can be constructed as follows: + +~~~ +List.Node(1, List.Node(2, List.Node(3, List.Empty))) +~~~ + +Enumerations Can Take Parameters +================================ + +~~~ +enum Vehicle(val numberOfWheels: Int) + case Unicycle extends Vehicle(1) + case Bicycle extends Vehicle(2) + case Car extends Vehicle(4) +~~~ + +- Enumeration cases have to use an explicit `extends` clause + +Exercise: Arithmetic Expressions +================================ + +Define a datatype modeling arithmetic expressions. + +An expression can be: + +- a number (e.g. `42`), +- a sum of two expressions, +- or, the product of two expressions. + +`Expr` Type Definition +====================== + +~~~ +enum Expr + case Number(n: Int) + case Sum(lhs: Expr, rhs: Expr) + case Prod(lhs: Expr, rhs: Expr) + +-> + +\begin{tabular}{ll} + \verb@val one = Expr.Number(1)@ \wsf one: Expr = Number(1) \\ + \verb@val two = Expr.Number(2)@ \wsf two: Expr = Number(2) \\ + \verb@Expr.Sum(one, two)@ \wsf Sum(Number(1), Number(2)) +\end{tabular} + + +Exercise +======== + +Is the following match expression valid? + +~~~ +expr match + case Expr.Sum(x, x) => Expr.Prod(2, x) + case e => e +~~~ + + O Yes + O No + +Exercise +======== + +Implement an `eval` operation that takes an `Expr` as parameter and +evaluates its value: + +~~~ +def eval(e: Expr): Int +~~~ + +Examples of use: + +\begin{tabular}{ll} + \verb@eval(Expr.Number(42))@ \wsf 42 \\ + \verb@eval(Expr.Prod(2, Expr.Sum(Expr.Number(8), Expr.Number(13))))@ \wsf 42 +\end{tabular} + +Exercise +======== + +Implement a `show` operation that takes an `Expr` as parameter and +returns a `String` representation of the operation. + +Be careful to introduce parenthesis when necessary! + +~~~ +def show(e: Expr): String +~~~ + +Examples of use: + +\begin{tabular}{ll} + \verb@show(Expr.Number(42))@ \wsf "42" \\ + \verb@show(Expr.Prod(2, Expr.Sum(Expr.Number(8), Expr.Number(13))))@ \wsf "2 * (8 + 13)" +\end{tabular} + +Summary +======= + +In this lecture, we have seen: + +- how to define data types accepting alternative values using + `enum` definitions, +- enumeration cases can be discriminated using pattern matching, +- enumeration cases can take parameters or be simple values, +- pattern matching allows us to extract information carried by an object. diff --git a/slides/sources/enum-2.md b/slides/sources/enum-2.md new file mode 100644 index 0000000000000000000000000000000000000000..348950bd3af6c4e92e0150db6d8ca56d950a71f3 --- /dev/null +++ b/slides/sources/enum-2.md @@ -0,0 +1,115 @@ +% Business Domain Modeling +% +% + +Business Domain Modeling +======================== + +Enums and case classes together provide basic building blocks +for defining data types. + +They are typically used to **model the business domain** of a program. + +How to Model Things? +==================== + +Modeling consists of translating concepts from the real world +into data type definitions in the Scala world. + +Modeling requires you to think about what details of the real +world are relevant for your program. + +> The purpose of abstraction is not to be vague, but to create +> a new semantic level in which one can be absolutely precise. +> +> Edsger W. Dijkstra + + +Modeling Methodology (1) +======================== + +There is no systematic methodology: often, a same set of concepts +can be modeled in multiple ways. + +But here are some advice. + +-> + +- Identify the concepts (in general, nouns) that you are interested in +- Identify the relations between them + - Does a concept _belong to_ another one? + - e.g. “a rational number _has_ a numerator and a denominator†+ - e.g. “a sum _has_ a left summand and a right summand†+ - Does a concept _generalize_ another one? + - e.g. “‘functional programming’ _is_ a possible programming paradigm†+ - e.g. “an arithmetic operation can either _be_ a product or a sum†+ +Modeling Methodology (2) +======================== + +- Translate each concept into a type definition + - Concepts belonging to others become **fields** of case classes + or of enumeration cases + - Concepts generalizing others become **enumerations** +- Check that you can construct meaningful objects from your model + +Example: Painting Application +============================= + +An image is made of a shape, a stroke width and a color. + +Possible shapes are a square or a circle. A square has a side length. +A circle has a radius. + +A color has a blue component, a red component and a green component. +Each component of a color is an integer value (between 0 and 255). + +-> + +~~~ +case class Image(shape: Shape, strokeWidth: Int, color: Color) + +enum Shape { + case Square(side: Double) + case Circle(radius: Double) +} + +case class Color(red: Int, green: Int, blue: Int) +~~~ + +Example: Painting Application +============================= + +~~~ +case class Image(shape: Shape, strokeWidth: Int, color: Color) + +enum Shape + case Square(side: Double) + case Circle(radius: Double) + +case class Color(red: Int, green: Int, blue: Int) +~~~ + +A blue circle: + +~~~ +val blue = Color(0, 0, 255) +val blueCircle = Image(Circle(10), 1, blue) +~~~ + +Data Types Define a Space of Possible States +============================================ + +The domain model defines the rules for constructing +objects representing a possible state of a program. + +The more possible states, the more risks to have bugs +or to reach illegal states. + +Hint: restrict the space of possible states to the minimum. + +Summary +======= + +In this lecture we have seen how to model the business domain +of a program using enumerations and case classes.