Skip to content

Commit

Permalink
Add concept exercise valentines-day
Browse files Browse the repository at this point in the history
  • Loading branch information
pwadsworth committed Oct 1, 2022
1 parent ab58774 commit 43c55d8
Show file tree
Hide file tree
Showing 13 changed files with 315 additions and 0 deletions.
13 changes: 13 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,19 @@
"concepts": [
"pattern-matching-literals"
]
},
{
"slug": "valentines-day",
"name": "Valentines Day",
"uuid": "537d8df3-0b12-4dbe-aa86-65ae79c4de0e",
"prerequisites": [
"pattern-matching-literals",
"numbers"
],
"status": "beta",
"concepts": [
"algebraic-data-types"
]
}
],
"practice": [
Expand Down
29 changes: 29 additions & 0 deletions exercises/concept/valentines-day/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Hints

## 1. Define the approval

- [This page][ADT] shows how to define an algebraic data type.

## 2. Define the cuisine

- [This page][ADT] shows how to define an algebraic data type.

## 3. Define the movie genres

- [This page][ADT] shows how to define an algebraic data type.

## 4. Define the activity

- [This section][ADT-with-data] of the same page shows how to define an algebraic data types with associated data.

## 5. Rate the activity

- The best way to execute logic based on the activity's value is to use [case expressions][case-expression].
- Pattern matching an algebraic data type case provides access to its associated data.
- To add an additional condition to a pattern, you can use a [guard][guards] inside a case.
- If you want to catch all other possible values in one case, you can use the wildcard pattern `_`.

[ADT]: https://www.schoolofhaskell.com/school/starting-with-haskell/introduction-to-haskell/2-algebraic-data-types#enumeration-types
[ADT-with-data]: https://www.schoolofhaskell.com/school/starting-with-haskell/introduction-to-haskell/2-algebraic-data-types#beyond-enumerations
[case-expression]: https://www.schoolofhaskell.com/school/starting-with-haskell/introduction-to-haskell/2-algebraic-data-types#case-expessions
[guards]: https://learnyouahaskell.github.io/syntax-in-functions.html#guards-guards
60 changes: 60 additions & 0 deletions exercises/concept/valentines-day/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Instructions

In this exercise, it's Valentine's day and you are planning what to do with your partner. Your partner has lots of ideas, and is asking you to rate the ideas, in order to find the best activity.

The following ideas are proposed by your partner:

- Playing a board game
- Chill out
- Watch a movie
- Go to a restaurant
- Take a walk

You have six tasks to help choose your Valentine's day activity.

## 1. Define the approval

For each idea your partner proposes, you respond with one of three options: yes, no or maybe.

Define the `Approval` algebraic data type to represent these options for the following three cases: `Yes`, `No` or `Maybe`.

## 2. Define the cuisines

Your partner has selected two possible restaurants: one based on Korean cuisine and the other based on Turkish cuisine.

Define the `Cuisine` algebraic data type to represent these restaurants as the following two cases: `Korean` or `Turkish`.

## 3. Define the movie genres

There are tons of movies to choose from, so to narrow things down, your partner also lists their preferred genre.

Define the `Genre` algebraic data type to represent the following genres cases: `Crime`, `Horror`, `Romance` or `Thriller`.

## 4. Define the activity

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.

Define the `Activity` algebraic data type to represent these activity types:

- `BoardGame`: no associated data.
- `Chill`: no associated data.
- `Movie`: has its `Genre` as associated data.
- `Restaurant`: has its `Cuisine` as associated data.
- `Walk`: has an `Int` representing the number of kilometers to walk as associated data.

## 5. Rate the activity

Finally, you're ready to rate your partner's ideas. This is how you feel about your partner's idea:

- Playing a board game: no.
- Chill out: no.
- Watch a movie: yes if it is a romantic movie; otherwise, no.
- Go to a restaurant: yes if the cuisine is Korean, maybe if it is Turkish.
- Take a walk: yes if the walk is less than three kilometers; maybe if it is between three and five kilometers; otherwise, no.

Implement a function named `rateActivity` that takes an `Activity` value and returns the `Approval` based on the above sentiments. For example:

```haskell
rateActivity (Restaurant Turkish)
-- -> Maybe
```
37 changes: 37 additions & 0 deletions exercises/concept/valentines-day/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Introduction

An Algebraic Data Type (ADT) represents a fixed number of named cases. Each value of an ADT corresponds to exactly one of the named cases.

An ADT is defined using the `data` keyword, with cases separated by pipe (`|`) characters. 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_).

```haskell
data Season
= Spring
| Summer
| Autumn
| Winter
```

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.

```haskell
data Number
= NInt Int --'NInt' is the constructor for an Int Number.
| NFloat Float --'NFloat' is the constructor for an Float Number.
| Invalid --'Invalid' does not have data associated to it.
```

Creating a value for a specific case can be done by referring to its name (e.g, `NInt 22`). As case names are just constructor functions, associated data can be passed as a regular function argument.

ADTs have _structural equality_, which means that two values for the same case and with the same (optional) data are equivalent.

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:

```haskell
add1 :: Number -> String
add1 number =
case number of
NInt i -> show (i + 1)
NFloat f -> show (f + 1.0)
Invalid -> error "Invalid input"
```
2 changes: 2 additions & 0 deletions exercises/concept/valentines-day/.meta/DONT-TEST-STUB
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Teaches the basics of defining data types, and we found that people usually learn best when they have to write things from scratch.
Thus, the stub lacks the data type definitions for the tests.
21 changes: 21 additions & 0 deletions exercises/concept/valentines-day/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"blurb": "Learn about algebraic data types by deciding what activity to surprise your partner with on Valentines Day.",
"authors": [
"pwadsworth"
],
"forked_from": [
"fsharp/valentines-day"
],
"files": {
"solution": [
"src/ValentinesDay.hs",
"package.yaml"
],
"test": [
"test/Tests.hs"
],
"exemplar": [
".meta/exemplar/src/ValentinesDay.hs"
]
}
}
28 changes: 28 additions & 0 deletions exercises/concept/valentines-day/.meta/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Design

## Learning objectives

- Know what [Algebraic Data Types][ADT] (ADT) are.
- Know how ADTs are different from enums.
- Know how to define ADT, with and without data.
- Know how to pattern match on ADTs using [case expressions][case-expression].

## Out of scope

- Recursive ADT.
- Single type wrapper ADT.
- Active patterns.
- Adding members to ADT.
- `function` pattern shorthand notation.

## Concepts

- `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.

## Prerequisites

- `basics`: defining functions and scoping and using integers.
- `pattern-matching`: know how to do pattern matching.

[ADT]: https://www.schoolofhaskell.com/school/starting-with-haskell/introduction-to-haskell/2-algebraic-data-types#enumeration-types
[case-expression]: https://www.schoolofhaskell.com/school/starting-with-haskell/introduction-to-haskell/2-algebraic-data-types#case-expessions
18 changes: 18 additions & 0 deletions exercises/concept/valentines-day/.meta/exemplar/package.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: valentines-day
version: 1.0.0.0

dependencies:
- base

library:
exposed-modules: ValentinesDay
source-dirs: src
ghc-options: -Wall

tests:
test:
main: Tests.hs
source-dirs: test
dependencies:
- valentines-day
- hspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module ValentinesDay (rateActivity, Approval (..), Cuisine (..), Genre (..), Activity (..)) where

data Approval
= Yes
| No
| Maybe

data Cuisine
= Korean
| Turkish

data Genre
= Crime
| Horror
| Romance
| Thriller

data Activity
= BoardGame
| Chill
| Movie Genre
| Restaurant Cuisine
| Walk Int

rateActivity :: Activity -> Approval
rateActivity activity =
case activity of
Restaurant Korean -> Yes
Restaurant Turkish -> Maybe
Movie Romance -> Yes
Movie _ -> No
Walk kilometers
| kilometers < 3 -> Yes
| kilometers < 5 -> Maybe
_ -> No
19 changes: 19 additions & 0 deletions exercises/concept/valentines-day/package.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: valentines-day
version: 1.0.0.0

dependencies:
- base

library:
exposed-modules: ValentinesDay
source-dirs: src
ghc-options: -Wall


tests:
test:
main: Tests.hs
source-dirs: test
dependencies:
- valentines-day
- hspec
14 changes: 14 additions & 0 deletions exercises/concept/valentines-day/src/ValentinesDay.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module ValentinesDay where

-- Define the function and required algebraic data types (ADT) below.

data Approval = ImplementApproval

data Cuisine = ImplementCuisine

data Genre = ImplementGenre

data Activity = ImplementActivity

rateActivity :: Activity -> Approval
rateActivity activity = error "Implement rateActivity"
1 change: 1 addition & 0 deletions exercises/concept/valentines-day/stack.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
resolver: lts-18.14
38 changes: 38 additions & 0 deletions exercises/concept/valentines-day/test/Tests.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Test.Hspec (describe, hspec, it)
import ValentinesDay (Activity (..), Approval (..), Cuisine (..), Genre (..), rateActivity)

main :: IO ()
main = hspec $
describe "ValentinesDay" $ do
it "chill rated no" $
case rateActivity Chill of
No -> True
_ -> False
it "board game rated no" $
case rateActivity BoardGame of
No -> True
_ -> False
it "crime movie rated no" $
case rateActivity (Movie Crime) of
No -> True
_ -> False
it "horror movie rated no" $
case rateActivity (Movie Horror) of
No -> True
_ -> False
it "romance movie rated yes" $
case rateActivity (Movie Romance) of
Yes -> True
_ -> False
it "thriller movie rated no" $
case rateActivity (Movie Thriller) of
No -> True
_ -> False
it "korean restaurant rated no" $
case rateActivity (Restaurant Korean) of
Yes -> True
_ -> False
it "turkish restaurant rated maybe" $
case rateActivity (Restaurant Turkish) of
Maybe -> True
_ -> False

0 comments on commit 43c55d8

Please sign in to comment.