Skip to content

Commit 7e54949

Browse files
authored
Merge pull request #1110 from pwadsworth/ADTs
Concept Algebraic Data Types with exercise
2 parents f81ee7d + 7fcbf6b commit 7e54949

File tree

19 files changed

+447
-1
lines changed

19 files changed

+447
-1
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"authors": [
3+
"pwadsworth"
4+
],
5+
"blurb": "Introduction to Algebraic Data Types in Haskell."
6+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Introduction
2+
3+
An Algebraic Data Type (ADT) represents a fixed number of named cases.
4+
Each value of an ADT corresponds to exactly one of the named cases.
5+
6+
An ADT is defined using the `data` keyword, with cases separated by pipe (`|`) characters.
7+
If none of the cases have data associated with them the ADT is similar to what other languages usually refer to as an _enumeration_ (or _enum_).
8+
9+
```haskell
10+
data Season
11+
= Spring
12+
| Summer
13+
| Autumn
14+
| Winter
15+
```
16+
17+
Each case of an ADT can optionally have data associated with it, and different cases can have different types of data. When the case has data associated, a constructor is required.
18+
19+
```haskell
20+
data Number
21+
= NInt Int --'NInt' is the constructor for an Int Number.
22+
| NFloat Float --'NFloat' is the constructor for an Float Number.
23+
| Invalid --'Invalid' does not have data associated to it.
24+
```
25+
26+
Creating a value for a specific case can be done by referring to its name (e.g, `NInt 22`).
27+
As case names are just constructor functions, associated data can be passed as a regular function argument.
28+
29+
ADTs have _structural equality_, which means that two values for the same case and with the same (optional) data are equivalent.
30+
31+
While one can use `if/else` expressions to work with ADTs, the recommended way to work with them is through pattern matching using _case_ statement:
32+
33+
```haskell
34+
add1 :: Number -> String
35+
add1 number =
36+
case number of
37+
NInt i -> show (i + 1)
38+
NFloat f -> show (f + 1.0)
39+
Invalid -> error "Invalid input"
40+
```
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Introduction
2+
3+
An Algebraic Data Type (ADT) represents a fixed number of named cases.
4+
Each value of an ADT corresponds to exactly one of the named cases.
5+
6+
An ADT is defined using the `data` keyword, with cases separated by pipe (`|`) characters.
7+
If none of the cases have data associated with them the ADT is similar to what other languages usually refer to as an _enumeration_ (or _enum_).
8+
9+
```haskell
10+
data Season
11+
= Spring
12+
| Summer
13+
| Autumn
14+
| Winter
15+
```
16+
17+
Each case of an ADT can optionally have data associated with it, and different cases can have different types of data.
18+
When the case has data associated, a constructor is required.
19+
20+
```haskell
21+
data Number
22+
= NInt Int --'NInt' is the constructor for an Int Number.
23+
| NFloat Float --'NFloat' is the constructor for an Float Number.
24+
| Invalid --'Invalid' does not have data associated to it.
25+
```
26+
27+
Creating a value for a specific case can be done by referring to its name (e.g, `NInt 22`).
28+
As case names are just constructor functions, associated data can be passed as a regular function argument.
29+
30+
ADTs have _structural equality_, which means that two values for the same case and with the same (optional) data are equivalent.
31+
32+
While one can use `if/else` expressions to work with ADTs, the recommended way to work with them is through pattern matching using _case_ statement:
33+
34+
```haskell
35+
add1 :: Number -> String
36+
add1 number =
37+
case number of
38+
NInt i -> show (i + 1)
39+
NFloat f -> show (f + 1.0)
40+
Invalid -> error "Invalid input"
41+
```
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Introduction
2+
3+
%{concept:algebraic-data-types}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[
2+
{
3+
"url": "https://learnyouahaskell.github.io/making-our-own-types-and-typeclasses.html#algebraic-data-types",
4+
"description": "Algebraic Data Types"
5+
},
6+
{
7+
"url": "https://learnyouahaskell.github.io/making-our-own-types-and-typeclasses.html#type-parameters",
8+
"description": "Type variables"
9+
}
10+
]

config.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,19 @@
8383
"concepts": [
8484
"pattern-matching-literals"
8585
]
86+
},
87+
{
88+
"slug": "valentines-day",
89+
"name": "Valentines Day",
90+
"uuid": "537d8df3-0b12-4dbe-aa86-65ae79c4de0e",
91+
"prerequisites": [
92+
"pattern-matching-literals",
93+
"numbers"
94+
],
95+
"status": "beta",
96+
"concepts": [
97+
"algebraic-data-types"
98+
]
8699
}
87100
],
88101
"practice": [
@@ -1267,6 +1280,11 @@
12671280
"uuid": "ca21a553-6fc1-49be-8f47-730f97330862",
12681281
"slug": "pattern-matching-literals",
12691282
"name": "Pattern Matching Literals"
1283+
},
1284+
{
1285+
"uuid": "cb756cfd-9b5c-408d-bf3f-b2adc8322da3",
1286+
"slug": "algebraic-data-types",
1287+
"name": "Algebraic Data Types"
12701288
}
12711289
],
12721290
"key_features": [
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Hints
2+
3+
## 1. Define the approval
4+
5+
- [Define the algebraic data type][ADT] `Approval` with constructors for the required options.
6+
7+
## 2. Define the cuisine
8+
9+
- [Define the algebraic data type][ADT] `Cuisine` with constructors for the required options.
10+
11+
## 3. Define the movie genres
12+
13+
- [Define the algebraic data type][ADT] `Genre` with constructors for the required options.
14+
15+
## 4. Define the activity
16+
17+
- [Define an algebraic data type with associated data][ADT-with-data] to encapsulate the different activities.
18+
19+
## 5. Rate the activity
20+
21+
- The best way to execute logic based on the activity's value is to use [case expressions][case-expression].
22+
- Pattern matching an algebraic data type case provides access to its associated data.
23+
- To add an additional condition to a pattern, you can use a [guard][guards] inside a case.
24+
- If you want to catch all other possible values in one case, you can use the wildcard pattern `_`.
25+
26+
[ADT]: https://www.schoolofhaskell.com/school/starting-with-haskell/introduction-to-haskell/2-algebraic-data-types#enumeration-types
27+
[ADT-with-data]: https://www.schoolofhaskell.com/school/starting-with-haskell/introduction-to-haskell/2-algebraic-data-types#beyond-enumerations
28+
[case-expression]: https://www.schoolofhaskell.com/school/starting-with-haskell/introduction-to-haskell/2-algebraic-data-types#case-expessions
29+
[guards]: https://learnyouahaskell.github.io/syntax-in-functions.html#guards-guards
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Instructions
2+
3+
In this exercise, it's Valentine's day and you are planning what to do with your partner.
4+
Your partner has lots of ideas, and is asking you to rate the ideas, in order to find the best activity.
5+
6+
The following ideas are proposed by your partner:
7+
8+
- Playing a board game
9+
- Chill out
10+
- Watch a movie
11+
- Go to a restaurant
12+
- Take a walk
13+
14+
You have six tasks to help choose your Valentine's day activity.
15+
16+
## 1. Define the approval
17+
18+
For each idea your partner proposes, you respond with one of three options: yes, no or maybe.
19+
20+
Define the `Approval` algebraic data type to represent these options for the following three cases: `Yes`, `No` or `Maybe`.
21+
22+
## 2. Define the cuisines
23+
24+
Your partner has selected two possible restaurants: one based on Korean cuisine and the other based on Turkish cuisine.
25+
26+
Define the `Cuisine` algebraic data type to represent these restaurants as the following two cases: `Korean` or `Turkish`.
27+
28+
## 3. Define the movie genres
29+
30+
There are tons of movies to choose from, so to narrow things down, your partner also lists their preferred genre.
31+
32+
Define the `Genre` algebraic data type to represent the following genres cases: `Crime`, `Horror`, `Romance` or `Thriller`.
33+
34+
## 4. Define the activity
35+
36+
As mentioned, your partner has come up with five possible activities: playing a board game, chill out, watch a movie, go to a restaurant and taking a walk.
37+
38+
Define the `Activity` algebraic data type to represent these activity types:
39+
40+
- `BoardGame`: no associated data.
41+
- `Chill`: no associated data.
42+
- `Movie`: has its `Genre` as associated data.
43+
- `Restaurant`: has its `Cuisine` as associated data.
44+
- `Walk`: has an `Int` representing the number of kilometers to walk as associated data.
45+
46+
## 5. Rate the activity
47+
48+
Finally, you're ready to rate your partner's ideas.
49+
This is how you feel about your partner's idea:
50+
51+
- Playing a board game: no.
52+
- Chill out: no.
53+
- Watch a movie: yes if it is a romantic movie; otherwise, no.
54+
- Go to a restaurant: yes if the cuisine is Korean, maybe if it is Turkish.
55+
- Take a walk: yes if the walk is less than three kilometers; maybe if it is between three and five kilometers (inclusive); otherwise, no.
56+
57+
Implement a function named `rateActivity` that takes an `Activity` value and returns the `Approval` based on the above sentiments.
58+
For example:
59+
60+
```haskell
61+
rateActivity (Restaurant Turkish)
62+
-- -> Maybe
63+
```
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Introduction
2+
3+
An Algebraic Data Type (ADT) represents a fixed number of named cases.
4+
Each value of an ADT corresponds to exactly one of the named cases.
5+
6+
An ADT is defined using the `data` keyword, with cases separated by pipe (`|`) characters.
7+
If none of the cases have data associated with them the ADT is similar to what other languages usually refer to as an _enumeration_ (or _enum_).
8+
9+
```haskell
10+
data Season
11+
= Spring
12+
| Summer
13+
| Autumn
14+
| Winter
15+
```
16+
17+
Each case of an ADT can optionally have data associated with it, and different cases can have different types of data.
18+
When the case has data associated, a constructor is required.
19+
20+
```haskell
21+
data Number
22+
= NInt Int --'NInt' is the constructor for an Int Number.
23+
| NFloat Float --'NFloat' is the constructor for an Float Number.
24+
| Invalid --'Invalid' does not have data associated to it.
25+
```
26+
27+
Creating a value for a specific case can be done by referring to its name (e.g, `NInt 22`).
28+
As case names are just constructor functions, associated data can be passed as a regular function argument.
29+
30+
ADTs have _structural equality_, which means that two values for the same case and with the same (optional) data are equivalent.
31+
32+
While one can use `if/else` expressions to work with ADTs, the recommended way to work with them is through pattern matching using _case_ statement:
33+
34+
```haskell
35+
add1 :: Number -> String
36+
add1 number =
37+
case number of
38+
NInt i -> show (i + 1)
39+
NFloat f -> show (f + 1.0)
40+
Invalid -> error "Invalid input"
41+
```
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Teaches the basics of defining data types, and we found that people usually learn best when they have to write things from scratch.
2+
Thus, the stub lacks the data type definitions for the tests.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"blurb": "Learn about algebraic data types by deciding what activity to surprise your partner with on Valentines Day.",
3+
"authors": [
4+
"pwadsworth"
5+
],
6+
"forked_from": [
7+
"fsharp/valentines-day"
8+
],
9+
"files": {
10+
"solution": [
11+
"src/ValentinesDay.hs",
12+
"package.yaml"
13+
],
14+
"test": [
15+
"test/Tests.hs"
16+
],
17+
"exemplar": [
18+
".meta/exemplar/src/ValentinesDay.hs"
19+
],
20+
"invalidator": [
21+
"stack.yaml"
22+
]
23+
}
24+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Design
2+
3+
## Learning objectives
4+
5+
- Know what [Algebraic Data Types][ADT] (ADT) are.
6+
- Know how ADTs are different from enums.
7+
- Know how to define ADT, with and without data.
8+
- Know how to pattern match on ADTs using [case expressions][case-expression].
9+
10+
## Out of scope
11+
12+
- Recursive ADT.
13+
- Single type wrapper ADT.
14+
- Active patterns.
15+
- Adding members to ADT.
16+
- `function` pattern shorthand notation.
17+
18+
## Concepts
19+
20+
- `Algebraic Data Types`: know what ADTs are; know how ADTs are different from enums; know how to define an ADT, with and without data; know how to pattern match on ADTs.
21+
22+
## Prerequisites
23+
24+
- `basics`: defining functions and scoping and using integers.
25+
- `pattern-matching`: know how to do pattern matching.
26+
27+
[ADT]: https://www.schoolofhaskell.com/school/starting-with-haskell/introduction-to-haskell/2-algebraic-data-types#enumeration-types
28+
[case-expression]: https://www.schoolofhaskell.com/school/starting-with-haskell/introduction-to-haskell/2-algebraic-data-types#case-expessions
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: valentines-day
2+
version: 1.0.0.0
3+
4+
dependencies:
5+
- base
6+
7+
library:
8+
exposed-modules: ValentinesDay
9+
source-dirs: src
10+
ghc-options: -Wall
11+
12+
tests:
13+
test:
14+
main: Tests.hs
15+
source-dirs: test
16+
dependencies:
17+
- valentines-day
18+
- hspec
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
module ValentinesDay (rateActivity, Approval (..), Cuisine (..), Genre (..), Activity (..)) where
2+
3+
data Approval
4+
= Yes
5+
| No
6+
| Maybe
7+
8+
data Cuisine
9+
= Korean
10+
| Turkish
11+
12+
data Genre
13+
= Crime
14+
| Horror
15+
| Romance
16+
| Thriller
17+
18+
data Activity
19+
= BoardGame
20+
| Chill
21+
| Movie Genre
22+
| Restaurant Cuisine
23+
| Walk Int
24+
25+
rateActivity :: Activity -> Approval
26+
rateActivity activity =
27+
case activity of
28+
Restaurant Korean -> Yes
29+
Restaurant Turkish -> Maybe
30+
Movie Romance -> Yes
31+
Movie _ -> No
32+
Walk kilometers
33+
| kilometers < 3 -> Yes
34+
| kilometers <= 5 -> Maybe
35+
_ -> No

0 commit comments

Comments
 (0)