Skip to content

Commit

Permalink
Add concept exercise: dnd-char / kwdef, using-macros (JuliaLang#381)
Browse files Browse the repository at this point in the history
* Deprecate v2 exercise dnd-character

There will be a v3 exercise built on this exercise.

* Add concept exercise: dnd-char / kwdef, using-macros

* Add vectors pre-req

Required to understand the examplar solution, and at this point it's very unlikely a student won't have solved it anyway

* Fix exemplar config key

* Apply suggestions from code review

Co-authored-by: Colin Caine <[email protected]>

* Apply more suggestions

Co-authored-by: Colin Caine <[email protected]>
  • Loading branch information
SaschaMann and cmcaine authored Apr 28, 2021
1 parent 03e5f34 commit 7d37c32
Show file tree
Hide file tree
Showing 12 changed files with 732 additions and 1 deletion.
9 changes: 9 additions & 0 deletions concepts/kwdef/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"blurb": "The macro `Base.@kwdef` can be used to automatically define constructors that take keyword arguments for type definitions.",
"authors": [
"SaschaMann"
],
"contributors": [
"cmcaine"
]
}
133 changes: 133 additions & 0 deletions concepts/kwdef/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# About

The macro `Base.@kwdef` can be used to automatically define constructors that take keyword arguments for the definition of a [struct][struct].
These keyword argument constructors can also set default values for fields.

For example, the following definition

```julia
Base.@kwdef struct HumanStats
birthyear::Int
height::Int
mass::Int
bmi::Float64 = mass / (0.01 * height)^2
end
```

defines three constructors at once

```julia
julia> methods(HumanStats)
# 3 methods for type constructor:
[1] HumanStats(; birthyear, height, mass, bmi) [...]
[2] HumanStats(birthyear::Int64, height::Int64, mass::Int64, bmi::Float64) [...]
[3] HumanStats(birthyear, height, mass, bmi) [...]
```

including a constructor based on keyword arguments

```julia
julia> HumanStats(
birthyear = 2002,
height = 193,
mass = 83
)
HumanStats(2002, 193, 83, 22.28247738194314)
```

whereas the standard definition

```julia
struct HumanStats
birthyear::Int
height::Int
mass::Int
bmi::Float64
end
```

only defines the following two

```julia
julia> methods(HumanStats)
# 2 methods for type constructor:
[1] HumanStats(birthyear::Int64, height::Int64, mass::Int64, bmi::Float64) [...]
[2] HumanStats(birthyear, height, mass, bmi) [...]
```

and would require manually defining a keyword-based constructor if you wanted one.

## When to use `Base.@kwdef`?

Keyword-based constructors are particularly useful for constructing structs that take a large number of arguments, in particular when the type or value of the arguments don't directly relate to the arguments' meanings.

Consider the following definition:

```julia
dirk = BasketballPlayer(1978, 1994, 2019, 213, 111, 42, 1998, [2011], 31560, 11489, 3651)
```

It is not clear what each value means without knowing the definition of the type.
And even in case one knows the definition, one would probably have to look up the order over and over again.

However, when using a keyboard-based constructor, their meaning is immediately obvious:

```julia
dirk = BasketballPlayer(
birthyear = 1978,
career_start = 1994,
career_end = 2019,
height = 213,
weight = 111,
number = 42,
drafted = 1998,
championships = [2011],
points = 31560,
rebounds = 11489,
assists = 3651
)
```

<!-- The following type definition was used for the examples above:
```julia
struct BasketballPlayer
birthyear::Int
career_start::Int
career_end::Int
height::Int
weight::Int
number::Int
drafted::Int
championships::Vector{Int}
points::Int
rebounds::Int
assists::Int
end
```
-->

<!-- TODO Maybe mention named tuples as an alternative? -->

~~~~exercism/caution
`Base.@kwdef` is not exported and not documented in the manual, and therefore not part of [Julia's public API][public-api].
This means its interface is technically not guaranteed to be stable and may change between minor Julia updates.
However, the macro is widely used across the ecosystem, and it's unlikely that it will be changed in a breaking way.
For more information, see:
- ["What does `@kwdef` do?"][discourse-kwdef]
- [Julia Issue #32659 to add `@kwdef` to the Manual][add-kwdef-to-manual]
- [Julia Issue #33192 to export `@kwdef`][export-kwdef]
~~~~

## Sources

1. Dirk Nowitzki, Wikipedia. (2021). https://en.wikipedia.org/w/index.php?title=Dirk_Nowitzki&oldid=1019583359 (accessed April 24, 2021).

[struct]: https://exercism.io/tracks/julia/concepts/structs
[discourse-kwdef]: https://discourse.julialang.org/t/what-does-kwdef-do/51973/2
[public-api]: https://github.com/JuliaLang/julia/pull/35715/files#diff-3591da1fafe13d9fcae96e5b32a9492c6fb68b7e33ac0810b106b09b45d59534
[add-kwdef-to-manual]: https://github.com/JuliaLang/julia/issues/32659
[export-kwdef]: https://github.com/JuliaLang/julia/issues/33192
132 changes: 132 additions & 0 deletions concepts/kwdef/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Introduction

The macro `Base.@kwdef` can be used to automatically define constructors that take keyword arguments for the definition of a [struct][struct].
It also allows one to set default values for fields.

For example, the following definition

```julia
Base.@kwdef struct HumanStats
birthyear::Int
height::Int
mass::Int
bmi::Float64 = mass / (0.01 * height)^2
end
```

defines three constructors at once

```julia
julia> methods(HumanStats)
# 3 methods for type constructor:
[1] HumanStats(; birthyear, height, mass, bmi) [...]
[2] HumanStats(birthyear::Int64, height::Int64, mass::Int64, bmi::Float64) [...]
[3] HumanStats(birthyear, height, mass, bmi) [...]
```

including a constructor based on keyword arguments

```julia
julia> HumanStats(
birthyear = 2002,
height = 193,
mass = 83
)
HumanStats(2002, 193, 83, 22.28247738194314)
```

whereas the standard definition

```julia
struct HumanStats
birthyear::Int
height::Int
mass::Int
bmi::Float64
end
```

only defines the following two

```julia
julia> methods(HumanStats)
# 2 methods for type constructor:
[1] HumanStats(birthyear::Int64, height::Int64, mass::Int64, bmi::Float64) [...]
[2] HumanStats(birthyear, height, mass, bmi) [...]
```

and would require manually defining a keyword-based constructor.

## When to use `Base.@kwdef`?

Keyword-based constructors are particularly useful for constructing structs that take a large number of arguments, in particular when the type or value of the arguments don't directly relate to the arguments' meanings.

Consider the following definition:

```julia
dirk = BasketballPlayer(1978, 1994, 2019, 213, 111, 42, 1998, [2011], 31560, 11489, 3651)
```

It is not clear what each value means without knowing the definition of the type.
And even in case one knows the definition, one would probably have to look up the order over and over again.

However, when using a keyboard-based constructor, their meaning is immediately obvious:

```julia
dirk = BasketballPlayer(
birthyear = 1978,
career_start = 1994,
career_end = 2019,
height = 213,
weight = 111,
number = 42,
drafted = 1998,
championships = [2011],
points = 31560,
rebounds = 11489,
assists = 3651
)
```

<!-- The following type definition was used for the examples above:
```julia
struct BasketballPlayer
birthyear::Int
career_start::Int
career_end::Int
height::Int
weight::Int
number::Int
drafted::Int
championships::Vector{Int}
points::Int
rebounds::Int
assists::Int
end
```
-->

~~~~exercism/caution
`Base.@kwdef` is not exported and not documented in the manual, and therefore not part of [Julia's public API][public-api].
This means its interface is technically not guaranteed to be stable and may change between minor Julia updates.
However, the macro is widely used across the ecosystem, and it's unlikely that it will be changed in a breaking way.
For more information, see:
- ["What does `@kwdef` do?"][discourse-kwdef]
- [Julia Issue #32659 to add `@kwdef` to the Manual][add-kwdef-to-manual]
- [Julia Issue #33192 to export `@kwdef`][export-kwdef]
~~~~

## Sources

1. Dirk Nowitzki, Wikipedia. (2021). https://en.wikipedia.org/w/index.php?title=Dirk_Nowitzki&oldid=1019583359 (accessed April 24, 2021).


[struct]: https://exercism.io/tracks/julia/concepts/structs
[discourse-kwdef]: https://discourse.julialang.org/t/what-does-kwdef-do/51973/2
[public-api]: https://github.com/JuliaLang/julia/pull/35715/files#diff-3591da1fafe13d9fcae96e5b32a9492c6fb68b7e33ac0810b106b09b45d59534
[add-kwdef-to-manual]: https://github.com/JuliaLang/julia/issues/32659
[export-kwdef]: https://github.com/JuliaLang/julia/issues/33192
18 changes: 18 additions & 0 deletions concepts/kwdef/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[
{
"description": "What does `@kwdef` do?",
"url": "https://discourse.julialang.org/t/what-does-kwdef-do/51973/2"
},
{
"description": "Julia Issue #32659 to add `@kwdef` to the Manual",
"url": "https://github.com/JuliaLang/julia/issues/32659"
},
{
"description": "Julia Issue #33192 to export `@kwdef`",
"url": "https://github.com/JuliaLang/julia/issues/33192"
},
{
"url": "https://github.com/JuliaLang/julia/pull/35715/files#diff-3591da1fafe13d9fcae96e5b32a9492c6fb68b7e33ac0810b106b09b45d59534",
"description": "Julia's Public API"
}
]
19 changes: 18 additions & 1 deletion config.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,22 @@
],
"status": "wip"
},
{
"uuid": "60e480e9-7f6f-460a-b753-f333a37d3e0c",
"name": "DND Character",
"slug": "dnd-char",
"concepts": [
"kwdef",
"using-macros"
],
"prerequisites": [
"immutable-structs",
"vectors",
"randomness",
"keyword-arguments"
],
"status": "wip"
},
{
"uuid": "f2f696e6-8a6a-4c8c-ae0b-d940d8579905",
"slug": "leap",
Expand Down Expand Up @@ -737,7 +753,8 @@
"randomness",
"math",
"test_driven_development"
]
],
"status": "deprecated"
},
{
"slug": "robot-simulator",
Expand Down
Loading

0 comments on commit 7d37c32

Please sign in to comment.