From d4e78f1a2742aee6cedab6214df04b792569bfce Mon Sep 17 00:00:00 2001 From: Cedd Burge Date: Tue, 18 May 2021 21:19:57 +0100 Subject: [PATCH] Add custom type concept exercise --- .../concept/valentines-day/.docs/hints.md | 16 ++++++ .../valentines-day/.docs/instructions.md | 57 +++++++++++++++++++ .../valentines-day/.docs/introduction.md | 50 ++++++++++++++++ .../concept/valentines-day/.meta/Exemplar.elm | 39 +++++++++++++ .../concept/valentines-day/.meta/config.json | 15 +++++ .../concept/valentines-day/.meta/design.md | 20 +++++++ exercises/concept/valentines-day/elm.json | 28 +++++++++ .../valentines-day/src/ValentinesDay.elm | 4 ++ .../concept/valentines-day/tests/Tests.elm | 43 ++++++++++++++ 9 files changed, 272 insertions(+) create mode 100644 exercises/concept/valentines-day/.docs/hints.md create mode 100644 exercises/concept/valentines-day/.docs/instructions.md create mode 100644 exercises/concept/valentines-day/.docs/introduction.md create mode 100644 exercises/concept/valentines-day/.meta/Exemplar.elm create mode 100644 exercises/concept/valentines-day/.meta/config.json create mode 100644 exercises/concept/valentines-day/.meta/design.md create mode 100644 exercises/concept/valentines-day/elm.json create mode 100644 exercises/concept/valentines-day/src/ValentinesDay.elm create mode 100644 exercises/concept/valentines-day/tests/Tests.elm diff --git a/exercises/concept/valentines-day/.docs/hints.md b/exercises/concept/valentines-day/.docs/hints.md new file mode 100644 index 00000000..a7f153ef --- /dev/null +++ b/exercises/concept/valentines-day/.docs/hints.md @@ -0,0 +1,16 @@ +# Hints + +## 1. Define the approval + +- [Custom Types][custom-types] shows how to define a new Custom Type. + +## 4. Define the activity + +- [Custom Types][custom-types] shows how to define a new Custom Type with associated data + +## 5. Rate the activity + +- The best way to execute logic based on the activity's value is to use [pattern matching][pattern-matching]. + +[custom-types]: https://guide.elm-lang.org/types/custom_types.html +[pattern-matching]: https://guide.elm-lang.org/types/pattern_matching.html diff --git a/exercises/concept/valentines-day/.docs/instructions.md b/exercises/concept/valentines-day/.docs/instructions.md new file mode 100644 index 00000000..3584f0e5 --- /dev/null +++ b/exercises/concept/valentines-day/.docs/instructions.md @@ -0,0 +1,57 @@ +# Instructions + +In this exercise, it's Valentine's day and you and your partner are planning on doing something nice together. Your partner has lots of ideas, and is now asking you to rate the ideas, in order to find the activity to engage in. + +The following ideas are proposed by your partner: + +- Playing a board game +- Chill out +- Watch a movie +- Go to a restaurant + +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` Custom Type to represent these options as the following three cases: `Yes`, `No` and `Maybe`. + +## 2. Define the cuisines + +Your partner has selected two possible restaurants: one based on the Korean cuisine and the other based on the Turkish cuisine. + +Define the `Cuisine` Custom Type to represent these cuisines as the following two cases: `Korean` and `Turkish`. + +## 3. Define the movie genres + +There are tons of movies to choose from, so to narrow things down, your partner also lists their genre. + +Define the `Genre` Custom Type to represent the following genres as cases: `Crime`, `Horror`, `Romance` and `Thriller`. + +## 4. Define the activity + +As said, your partner has come up with five different activities: playing a board game, chill out, watch a movie and go to a restaurant. + +Define the `Activity` Custom 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. + +## 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 is is a romantic movie; otherwise, no. +- Go to a restaurant: yes if the cuisine is Korean, maybe if it is Turkish. + +Implement a function named `rateActivity` that takes an `Activity` value and returns the `Approval` based on the above sentiments: + +```elm +rateActivity (Restaurant Turkish) +-- => Maybe +``` diff --git a/exercises/concept/valentines-day/.docs/introduction.md b/exercises/concept/valentines-day/.docs/introduction.md new file mode 100644 index 00000000..60b45f37 --- /dev/null +++ b/exercises/concept/valentines-day/.docs/introduction.md @@ -0,0 +1,50 @@ +# Introduction + +[Custom Types][custom-types] in Elm represent a fixed number of named cases. Each value corresponds to exactly one of the named cases. + +A Custom Type is defined using the `type` keyword and requires very little syntax. They are one of the most important techniques in Elm programming, and are used to make the possible values in code exactly match the valid values in real life, which leaves no room for invalid data, and makes [impossible states impossible to represent in the code][impossible-states]. + +```elm +type Season + = Spring + | Summer + | Autumn + | Winter +``` + +Each case of a custom type can optionally have data associated with it, and different cases can have different types of data. If none of the cases have data associated with them, the discriminated union is similar to what other languages usually refer to as an _enumeration_ (or _enum_). + +```elm +type FlexibleNumber + = Integer Int + | Float Float + | Invalid +``` + +Creating a value for a specific case can be done by referring to its name (`Spring`), when it is defined in the same module, or is imported with the `import SeasonModule exposing (Season(..))` style syntax, or by referring to its fully qualified name (`Season.Spring`) when imported with `import SeasonModule exposing (Season)` style syntax. + +```elm +integerTwo = Integer 2 +invalid = FlexibleNumber.Invalid +``` + +Custom types, along with everything in Elm, have _structural equality_, which means that two values for the same case and with the same (optional) data are equivalent. + +The preferred way to work with custom types is with [pattern matching][pattern-matching]: + +```elm +let describe flexibleNumber = + case flexibleNumber of + Integer i -> + "Integer: " ++ fromInt(i) + + Float f -> + "Float: " ++ fromFloat(f) + + Invalid -> + "Invalid" +``` + +[custom-types]: https://guide.elm-lang.org/types/custom_types.html +[pattern-matching]: https://guide.elm-lang.org/types/pattern_matching.html +[impossible-states]: https://www.youtube.com/watch?v=IcgmSRJHu_8 diff --git a/exercises/concept/valentines-day/.meta/Exemplar.elm b/exercises/concept/valentines-day/.meta/Exemplar.elm new file mode 100644 index 00000000..4a88ef96 --- /dev/null +++ b/exercises/concept/valentines-day/.meta/Exemplar.elm @@ -0,0 +1,39 @@ +module ValentinesDay exposing (..) + +type Approval = + Yes + | No + | Maybe + +type Cuisine = + Korean + | Turkish + +type Genre = + Crime + | Horror + | Romance + | Thriller + +type Activity = + BoardGame + | Chill + | Movie Genre + | Restaurant Cuisine + +rateActivity : Activity -> Approval +rateActivity activity = + case activity of + Restaurant Korean -> + Yes + + Restaurant Turkish -> + Maybe + + Movie Romance -> + Yes + + Movie _ -> + No + + _ -> No diff --git a/exercises/concept/valentines-day/.meta/config.json b/exercises/concept/valentines-day/.meta/config.json new file mode 100644 index 00000000..9aca375c --- /dev/null +++ b/exercises/concept/valentines-day/.meta/config.json @@ -0,0 +1,15 @@ +{ + "blurb": "Learn usage of Custom Types while deciding what to do for Valentines Day", + "authors": [ + "ceddlyburge" + ], + "contributors": [ + "mpizenberg" + ], + "forked_from": ["fsharp/valentines-day"], + "files": { + "solution": ["src/ValentinesDay.elm"], + "test": ["tests/Tests.elm"], + "exemplar": [".meta/Exemplar.elm"] + } +} diff --git a/exercises/concept/valentines-day/.meta/design.md b/exercises/concept/valentines-day/.meta/design.md new file mode 100644 index 00000000..b25ee3b9 --- /dev/null +++ b/exercises/concept/valentines-day/.meta/design.md @@ -0,0 +1,20 @@ +# Design + +## Learning objectives + +- Know what Custom Types are. +- Know how to define a Custom Type, with and without data. +- Know how to pattern match on Custom Types. + +## Out of scope + +- Opaque Types. +- Phantom Types. + +## Concepts + +- `custom-types`: know what custom types are; know how to define a custom type, with and without data; know how to pattern match on custom types. + +## Prerequisites + +- `basics-2`: Know the basic syntax of an Elm file. diff --git a/exercises/concept/valentines-day/elm.json b/exercises/concept/valentines-day/elm.json new file mode 100644 index 00000000..e08a5bb6 --- /dev/null +++ b/exercises/concept/valentines-day/elm.json @@ -0,0 +1,28 @@ +{ + "type": "application", + "source-directories": [ + "src" + ], + "elm-version": "0.19.1", + "dependencies": { + "direct": { + "elm/core": "1.0.5", + "elm/json": "1.1.3", + "elm/parser": "1.1.0", + "elm/random": "1.0.0", + "elm/regex": "1.0.0", + "elm/time": "1.0.0" + }, + "indirect": {} + }, + "test-dependencies": { + "direct": { + "elm-explorations/test": "1.2.2", + "rtfeldman/elm-iso8601-date-strings": "1.1.3" + }, + "indirect": { + "elm/html": "1.0.0", + "elm/virtual-dom": "1.0.2" + } + } +} diff --git a/exercises/concept/valentines-day/src/ValentinesDay.elm b/exercises/concept/valentines-day/src/ValentinesDay.elm new file mode 100644 index 00000000..9e89c4ed --- /dev/null +++ b/exercises/concept/valentines-day/src/ValentinesDay.elm @@ -0,0 +1,4 @@ +module ValentinesDay exposing (..) + +rateActivity activity = + Debug.todo "implement this function and create a type annotation" diff --git a/exercises/concept/valentines-day/tests/Tests.elm b/exercises/concept/valentines-day/tests/Tests.elm new file mode 100644 index 00000000..c36eac86 --- /dev/null +++ b/exercises/concept/valentines-day/tests/Tests.elm @@ -0,0 +1,43 @@ +module Tests exposing (tests) + +import Expect +import Test exposing (..) +import ValentinesDay exposing (..) + + +tests : Test +tests = + describe "ValentinesDay" + [ test "board game rated no" <| + \_ -> + rateActivity BoardGame + |> Expect.equal No + , test "chill rated no" <| + \_ -> + rateActivity Chill + |> Expect.equal No + , test "crime movie rated no" <| + \_ -> + rateActivity (Movie Crime) + |> Expect.equal No + , test "horror movie rated no" <| + \_ -> + rateActivity (Movie Horror) + |> Expect.equal No + , test "romance movie rated yes" <| + \_ -> + rateActivity (Movie Romance) + |> Expect.equal Yes + , test "thriller movie rated no" <| + \_ -> + rateActivity (Movie Thriller) + |> Expect.equal No + , test "korean restaurant rated no" <| + \_ -> + rateActivity (Restaurant Korean) + |> Expect.equal Yes + , test "turkish restaurant rated maybe" <| + \_ -> + rateActivity (Restaurant Turkish) + |> Expect.equal Maybe + ]