Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Custom types concept and associated exercise #368

Merged
merged 6 commits into from
Jun 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ The build and test script for this track lives at `bin/build.sh`, and uses npx,

TODO: link to the step-by-step guide instead here.

Version 3 of Exercism introduced Concepts and Concept Exercises, which are a completely new thing. There is a [dependency diagram](https://mermaid-js.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoiZ3JhcGggTFJcbiAgQmFzaWNzW01pbmltYWwgY29tcGlsYWJsZSBmaWxlXSAtLT4gSW5kZXBlbmRlbnRzW0ltcG9ydCwgRXhwb3NpbmcsIEZ1bmN0aW9ucywgRmxvYXQsIEludCwgVHlwZSBBbm5vdGF0aW9uc10gLS0-IE1hdGhlbWF0aWNhbC1PcGVyYXRvcnNcbiAgSW5kZXBlbmRlbnRzIC0tPiBNYXRoZW1hdGljYWwtT3BlcmF0b3JzXG4gIEluZGVwZW5kZW50cyAtLT4gTWF0aGVtYXRpY2FsLUZ1bmN0aW9uc1xuICBJbmRlcGVuZGVudHMgLS0-IExldC1FeHByZXNzaW9uc1xuICBJbmRlcGVuZGVudHMgLS0-IEVxdWFsaXR5XG4gIEVxdWFsaXR5IC0tPiBPcmRlcmluZ1xuICBPcmRlcmluZyAtLT4gQ29tcGFyaXNvbnNcbiAgSW5kZXBlbmRlbnRzIC0tPiBTdHJpbmdcbiAgU3RyaW5nIC0tPiBSZWdleFxuICBJbmRlcGVuZGVudHMgLS0-IENoYXJcbiAgSW5kZXBlbmRlbnRzIC0tPiBMaXN0XG4gIExpc3QgLS0-IExpc3QtRXh0cmFcbiAgSW5kZXBlbmRlbnRzIC0tPiBEaWN0XG4gIEluZGVwZW5kZW50cyAtLT4gU2V0XG4gIEluZGVwZW5kZW50cyAtLT4gQXJyYXlcbiAgSW5kZXBlbmRlbnRzIC0tPiBUeXBlLWFsaWFzXG4gIEluZGVwZW5kZW50cyAtLT4gU3VtLXR5cGVzXG4gIEluZGVwZW5kZW50cyAtLT4gQm9vbGVhbnNbQm9vbCB0eXBlIC8gb3BlcmF0b3JzLCBJZl1cbiAgU3VtLXR5cGVzIC0tPiBQYXR0ZXJuLW1hdGNoaW5nXG4gIFBhdHRlcm4tbWF0Y2hpbmcgLS0-IE1heWJlXG4gIEJvb2xlYW5zIC0tPiBNYXliZVxuICBNYXliZSAtLT4gUmVzdWx0XG4gIFBhdHRlcm4tbWF0Y2hpbmcgLS0-IFBhcnNlclxuICBJbmRlcGVuZGVudHMgLS0-IEZ1bmN0aW9uLWNvbXBvc2l0aW9uXG4gIEluZGVwZW5kZW50cyAtLT4gRnVuY3Rpb24tY2hhaW5pbmdcbiAgSW5kZXBlbmRlbnRzIC0tPiBQYXJ0aWFsLWFwcGxpY2F0aW9uXG4gIFBhcnRpYWwtYXBwbGljYXRpb24gLS0-IFBvaW50LWZyZWVzdHlsZVxuICBGdW5jdGlvbi1jb21wb3NpdGlvbiAtLT4gUG9pbnQtZnJlZXN0eWxlXG4gIEZ1bmN0aW9uLWNoYWluaW5nIC0tPiBQb2ludC1mcmVlc3R5bGVcbiAgUG9pbnQtZnJlZXN0eWxlIC0tPiBPcGVyYXRvci1mdW5jdGlvbnNcbiAgSW5kZXBlbmRlbnRzIC0tPiBCaXR3aXNlLW9wZXJhdG9yc1xuICBJbmRlcGVuZGVudHMgLS0-IENvbnZlcnNpb25zXG4gIEluZGVwZW5kZW50cyAtLT4gUG9zaXgtdGltZSIsIm1lcm1haWQiOnsidGhlbWUiOiJkZWZhdWx0In0sInVwZGF0ZUVkaXRvciI6ZmFsc2V9) showing all the Elm concepts. You can see all concepts currently defined in [`concepts/`](concepts/), and you can se all concept exercises defined in [`/exercises/concept`](/exercises/concept).
Version 3 of Exercism introduced Concepts and Concept Exercises, which are a completely new thing. There is a [dependency diagram](https://mermaid-js.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoiZ3JhcGggTFJcbiAgQmFzaWNzW01pbmltYWwgY29tcGlsYWJsZSBmaWxlXSAtLT4gSW5kZXBlbmRlbnRzW0ltcG9ydCwgRXhwb3NpbmcsIEZ1bmN0aW9ucywgRmxvYXQsIEludCwgVHlwZSBBbm5vdGF0aW9uc10gLS0-IE1hdGhlbWF0aWNhbC1PcGVyYXRvcnNcbiAgSW5kZXBlbmRlbnRzIC0tPiBNYXRoZW1hdGljYWwtRnVuY3Rpb25zXG4gIEluZGVwZW5kZW50cyAtLT4gTGV0LUV4cHJlc3Npb25zXG4gIEluZGVwZW5kZW50cyAtLT4gRXF1YWxpdHlcbiAgRXF1YWxpdHkgLS0-IE9yZGVyaW5nXG4gIE9yZGVyaW5nIC0tPiBDb21wYXJpc29uc1xuICBJbmRlcGVuZGVudHMgLS0-IFN0cmluZ1xuICBTdHJpbmcgLS0-IFJlZ2V4XG4gIEluZGVwZW5kZW50cyAtLT4gQ2hhclxuICBJbmRlcGVuZGVudHMgLS0-IExpc3RcbiAgTGlzdCAtLT4gTGlzdC1FeHRyYVxuICBJbmRlcGVuZGVudHMgLS0-IERpY3RcbiAgSW5kZXBlbmRlbnRzIC0tPiBTZXRcbiAgSW5kZXBlbmRlbnRzIC0tPiBBcnJheVxuICBJbmRlcGVuZGVudHMgLS0-IFR5cGUtYWxpYXNcbiAgSW5kZXBlbmRlbnRzIC0tPiBTdW0tdHlwZXNcbiAgSW5kZXBlbmRlbnRzIC0tPiBCb29sZWFuc1tCb29sIHR5cGUgLyBvcGVyYXRvcnMsIElmXVxuICBDdXN0b20tdHlwZXNbQ3VzdG9tLXR5cGVzIC8gUGF0dGVybi1tYXRjaGluZ10gLS0-IE1heWJlXG4gIEJvb2xlYW5zIC0tPiBNYXliZVxuICBNYXliZSAtLT4gUmVzdWx0XG4gIEN1c3RvbS10eXBlcyAtLT4gT3BhcXVlLVR5cGVzW09wYXF1ZSBUeXBlcyAvIFBhcnNlIC8gZG9udCB2YWxpZGF0ZV1cbiAgQ3VzdG9tLXR5cGVzIC0tPiBQaGFudG9tLVR5cGVzXG4gIEN1c3RvbS10eXBlcyAtLT4gUGFyc2VyXG4gIEluZGVwZW5kZW50cyAtLT4gRnVuY3Rpb24tY29tcG9zaXRpb25cbiAgSW5kZXBlbmRlbnRzIC0tPiBGdW5jdGlvbi1jaGFpbmluZ1xuICBJbmRlcGVuZGVudHMgLS0-IFBhcnRpYWwtYXBwbGljYXRpb25cbiAgUGFydGlhbC1hcHBsaWNhdGlvbiAtLT4gUG9pbnQtZnJlZXN0eWxlXG4gIEZ1bmN0aW9uLWNvbXBvc2l0aW9uIC0tPiBQb2ludC1mcmVlc3R5bGVcbiAgRnVuY3Rpb24tY2hhaW5pbmcgLS0-IFBvaW50LWZyZWVzdHlsZVxuICBQb2ludC1mcmVlc3R5bGUgLS0-IE9wZXJhdG9yLWZ1bmN0aW9uc1xuICBJbmRlcGVuZGVudHMgLS0-IEJpdHdpc2Utb3BlcmF0b3JzXG4gIEluZGVwZW5kZW50cyAtLT4gQ29udmVyc2lvbnNcbiAgSW5kZXBlbmRlbnRzIC0tPiBQb3NpeC10aW1lIiwibWVybWFpZCI6eyJ0aGVtZSI6ImRlZmF1bHQifSwidXBkYXRlRWRpdG9yIjpmYWxzZX0) showing all the Elm concepts. You can see all concepts currently defined in [`concepts/`](concepts/), and you can se all concept exercises defined in [`/exercises/concept`](/exercises/concept).

We would love some help creating more of these concepts / concept exercises.
To do so, it is probably easiest to copy and paste an existing Concept and Concept Exercise. You will also need to add the meta data for these in [`config.json`][config-json], and again, the easiest way is to copy and edit an existing entry.
Expand Down
9 changes: 9 additions & 0 deletions concepts/custom-types/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"blurb": "Learn how to use custom types in an Elm program",
"authors": [
"ceddlyburge"
],
"contributors": [
"mpizenberg"
]
}
50 changes: 50 additions & 0 deletions concepts/custom-types/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# About

[Custom Types][custom-types] in Elm represent a fixed number of named cases. Each value corresponds to exactly one of the named cases. In other languages / contexts these are known as _sum types_, _discriminated unions_, _tagged unions_ and _algebraic data types_.

Each case of a discriminated union 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_).

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
-- Custom Type without associated data
type Season
= Spring
| Summer
| Autumn
| Winter

-- Custom Type with associated data
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
50 changes: 50 additions & 0 deletions concepts/custom-types/introduction.md
Original file line number Diff line number Diff line change
@@ -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
14 changes: 14 additions & 0 deletions concepts/custom-types/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"url": "https://guide.elm-lang.org/types/custom_types.html",
"description": "Custom Types"
},
{
"url": "https://guide.elm-lang.org/types/pattern_matching.html",
"description": "Pattern matching"
},
{
"url": "https://www.youtube.com/watch?v=IcgmSRJHu_8",
"description": "Making impossible states impossible"
}
]
16 changes: 16 additions & 0 deletions exercises/concept/valentines-day/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -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
57 changes: 57 additions & 0 deletions exercises/concept/valentines-day/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -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
```
50 changes: 50 additions & 0 deletions exercises/concept/valentines-day/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -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
42 changes: 42 additions & 0 deletions exercises/concept/valentines-day/.meta/Exemplar.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
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

_ ->
No
15 changes: 15 additions & 0 deletions exercises/concept/valentines-day/.meta/config.json
Original file line number Diff line number Diff line change
@@ -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"]
}
}
20 changes: 20 additions & 0 deletions exercises/concept/valentines-day/.meta/design.md
Original file line number Diff line number Diff line change
@@ -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.
28 changes: 28 additions & 0 deletions exercises/concept/valentines-day/elm.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
5 changes: 5 additions & 0 deletions exercises/concept/valentines-day/src/ValentinesDay.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module ValentinesDay exposing (..)


rateActivity activity =
Debug.todo "implement this function and create a type annotation"
Loading