Skip to content

Commit b16c6bb

Browse files
pwadsworthpetertseng
authored andcommitted
Add concept exercise valentines-day
1 parent 122e1e8 commit b16c6bb

File tree

14 files changed

+326
-1
lines changed

14 files changed

+326
-1
lines changed

config.json

+13
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": [
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
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; 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+
```
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+
```
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.
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+
}
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
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
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
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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+
13+
tests:
14+
test:
15+
main: Tests.hs
16+
source-dirs: test
17+
dependencies:
18+
- valentines-day
19+
- hspec
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module ValentinesDay where
2+
3+
-- Define the function and required algebraic data types (ADT) below.
4+
5+
data Approval = ImplementApproval
6+
7+
data Cuisine = ImplementCuisine
8+
9+
data Genre = ImplementGenre
10+
11+
data Activity = ImplementActivity
12+
13+
rateActivity :: Activity -> Approval
14+
rateActivity activity = error "Implement rateActivity"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
resolver: lts-19.27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import Test.Hspec (describe, hspec, it)
2+
import ValentinesDay (Activity (..), Approval (..), Cuisine (..), Genre (..), rateActivity)
3+
4+
main :: IO ()
5+
main = hspec $
6+
describe "ValentinesDay" $ do
7+
it "chill rated no" $
8+
case rateActivity Chill of
9+
No -> True
10+
_ -> False
11+
it "board game rated no" $
12+
case rateActivity BoardGame of
13+
No -> True
14+
_ -> False
15+
it "crime movie rated no" $
16+
case rateActivity (Movie Crime) of
17+
No -> True
18+
_ -> False
19+
it "horror movie rated no" $
20+
case rateActivity (Movie Horror) of
21+
No -> True
22+
_ -> False
23+
it "romance movie rated yes" $
24+
case rateActivity (Movie Romance) of
25+
Yes -> True
26+
_ -> False
27+
it "thriller movie rated no" $
28+
case rateActivity (Movie Thriller) of
29+
No -> True
30+
_ -> False
31+
it "korean restaurant rated no" $
32+
case rateActivity (Restaurant Korean) of
33+
Yes -> True
34+
_ -> False
35+
it "turkish restaurant rated maybe" $
36+
case rateActivity (Restaurant Turkish) of
37+
Maybe -> True
38+
_ -> False

reference/concepts.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ The Haskell concept exercises are based on concepts. The list below contains the
8787
- [ ] Queues
8888
- [ ] Sets
8989
- [ ] Stacks
90-
- [ ] Algebraic data types
90+
- [x] Algebraic data types
9191
- [ ] Numbers (`Num` & Co.)
9292
- [x] Floating point numbers
9393
- [x] Signed integers (`Int`, `Integer`)

0 commit comments

Comments
 (0)