diff --git a/.editorconfig b/.editorconfig
index 1d7d564d..6b5913b5 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -8,10 +8,10 @@ trim_trailing_whitespace = true
[*.md]
indent_size = 2
indent_style = space
-max_line_length = 100 # Please keep this in sync with bin/lesson_check.py!
+max_line_length = 79 # Please keep this in sync with bin/lesson_check.py!
[*.r]
-max_line_length = 80
+max_line_length = 79
[*.py]
indent_size = 4
diff --git a/.vscode/carpentries.snippet.code-snippets b/.vscode/carpentries.snippet.code-snippets
index 7a2dbd61..1d327791 100644
--- a/.vscode/carpentries.snippet.code-snippets
+++ b/.vscode/carpentries.snippet.code-snippets
@@ -1,11 +1,17 @@
{
- // Place your julia-novice workspace snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
- // description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
- // is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
- // used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
- // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
- // Placeholders with the same ids are connected.
- // Example:
+ // Place your julia-novice workspace snippets here. Each snippet is defined
+ // under a snippet name and has a scope, prefix, body and description. Add
+ // comma-separated ids of the languages where the snippet is applicable in
+ // the scope field. If scope is left empty or omitted, the snippet gets
+ // applied to all languages. The prefix is what is used to trigger the
+ // snippet and the body will be expanded and inserted.
+ //
+ // Possible variables are:
+ // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label},
+ // ${2:another} for placeholders.
+ //
+ // Placeholders with the same ids are connected. Example:
+ //
// "Print to console": {
// "scope": "javascript,typescript",
// "prefix": "log",
@@ -15,6 +21,7 @@
// ],
// "description": "Log output to console"
// }
+
"julia-codefence": {
"prefix": "~~~jl",
"body": ["~~~",
@@ -31,16 +38,16 @@
"{: .output}"
]
},
- "challange and solution": {
+ "challenge and solution": {
"prefix": "challS",
"body": [
"> ## $1",
+ ">",
"> > ## ${2:Solution}",
- ">{: .solution}",
+ "> >",
+ "> {: .solution}",
"{: .challenge}",
"$0"
]
}
-
-
}
diff --git a/README.md b/README.md
index 3bb07c07..307926d6 100644
--- a/README.md
+++ b/README.md
@@ -1,44 +1,35 @@
-# julia-novice-trebuchet
+# Programming with Julia
-[![Create a Slack Account with us](https://img.shields.io/badge/Create_Slack_Account-The_Carpentries-071159.svg)](https://swc-slack-invite.herokuapp.com/)
+[![Create a Slack Account with us][slack]](https://swc-slack-invite.herokuapp.com/)
-[![Our channel](https://img.shields.io/badge/juliaswc-slack_channel-orange)](https://swcarpentry.slack.com/archives/CBJ8C7NE6)
+[![Our channel][channel](https://swcarpentry.slack.com/archives/CBJ8C7NE6)
-This lesson targets people who already have some experience with (scientific) programming, but are new to julia.
-
-
-**Thanks for contributing to The Carpentries Incubator!**
-This repository provides a blank starting point for lessons to be developed here.
-
-A member of the [Carpentries Curriculum Team](https://carpentries.org/team/)
-will work with you to get your lesson listed on the
-[Community Developed Lessons page][community-lessons]
-and make sure you have everything you need to begin developing your new lesson.
-
-##
+This lesson targets people who already have some experience with (scientific)
+programming, but are new to Julia.
## Contributing
-We welcome all contributions to improve the lesson! Maintainers will do their best to help you if you have any
-questions, concerns, or experience any difficulties along the way.
-
-We'd like to ask you to familiarize yourself with our [Contribution Guide](CONTRIBUTING.md) and have a look at
-the [more detailed guidelines][lesson-example] on proper formatting, ways to render the lesson locally, and even
-how to write new episodes.
+We welcome all contributions to improve the lesson! Maintainers will do their
+best to help you if you have any questions, concerns, or experience any
+difficulties along the way.
-Please see the current list of [issues](https://github.com/carpentries-incubator/julia-novice/issues) for ideas for contributing to this
-repository. For making your contribution, we use the GitHub flow, which is
-nicely explained in the chapter [Contributing to a Project](http://git-scm.com/book/en/v2/GitHub-Contributing-to-a-Project) in Pro Git
-by Scott Chacon.
-Look for the tag ![good_first_issue](https://img.shields.io/badge/-good%20first%20issue-gold.svg). This indicates that the maintainers will welcome a pull request fixing this issue.
+We'd like to ask you to familiarize yourself with our [Contribution
+Guide](CONTRIBUTING.md) and have a look at the [more detailed
+guidelines][lesson-example] on proper formatting, ways to render the lesson
+locally, and even how to write new episodes.
+Please see the current list of [issues][issues] for ideas for contributing to
+this repository. For making your contribution, we use the GitHub flow, which is
+nicely explained in the chapter [Contributing to a Project][start] in *Pro Git*
+by Scott Chacon. Look for the tag ![good_first_issue][first]. This indicates
+that the maintainers will welcome a pull request fixing this issue.
## Maintainer(s)
Current maintainers of this lesson are
-* [Simon](https://carpentries.org/instructors/#beastyblacksmith): [@BeastyBlacksmith](https://github.com/BeastyBlacksmith)
-
+* [Simon](https://carpentries.org/instructors/#beastyblacksmith):
+ [@BeastyBlacksmith](https://github.com/BeastyBlacksmith)
## Authors
@@ -46,8 +37,13 @@ A list of contributors to the lesson can be found in [AUTHORS](AUTHORS)
## Citation
-To cite this lesson, please consult with [CITATION](CITATION)
+To cite this lesson, please consult [CITATION](CITATION)
[cdh]: https://cdh.carpentries.org
[community-lessons]: https://carpentries.org/community-lessons
[lesson-example]: https://carpentries.github.io/lesson-example
+[slack]: https://img.shields.io/badge/Create_Slack_Account-The_Carpentries-071159.svg
+[channel]: https://img.shields.io/badge/juliaswc-slack_channel-orange
+[issues]: https://github.com/carpentries-incubator/julia-novice/issues
+[start]: http://git-scm.com/book/en/v2/GitHub-Contributing-to-a-Project
+[first]: https://img.shields.io/badge/-good%20first%20issue-gold.svg
diff --git a/_episodes/01-introduction.md b/_episodes/01-introduction.md
index f4c95756..2662d1aa 100644
--- a/_episodes/01-introduction.md
+++ b/_episodes/01-introduction.md
@@ -3,55 +3,90 @@ title: "Introduction"
teaching: 5
exercises: 0
questions:
-- "Who is teaching?"
-- "What is the schedule?"
+- "What is Julia?"
+- "Why use Julia?"
objectives:
-- "Explain the overall content and in more detail the content of todays lesson."
+- "Explain the difference between interpreted and compiled programming languages"
+- "Compare how composing works in Julia and some common programming languages"
keypoints:
-- "Julia is a just in time compiled language"
-- "Julia code composes well"
+- "Julia is a just-in-time compiled language"
+- "Julia packages compose well"
---
## What is a programming language?
-A programming language mediates between the natural language of humans and the machine instructions of a computer.
-The human specifies what the computer should compute on a high level using the programming language.
-This specification will be translated to machine instructions, the so called assembly code, which will be executed by the processor (CPU, GPU, ...).
+A programming language mediates between the natural language of humans and the
+machine instructions of a computer.
+The human specifies what the computer should compute on a high level using the
+programming language.
+This specification will be translated to machine instructions, the so called
+assembly code, which will be executed by the processor (CPU, GPU, ...).
## Interpreting and compiling
-This translation happens differently depending of the programming language you use.
+This translation happens differently depending on the programming language you
+use.
There are mainly two different techniques: _compiling_ and _interpreting_.
-Interpreted languages such as python and R translate instructions one at a time, while compiled languages like C and Fortran take whole documents analyze the structure of the code and perform optimizations before translating it to machine code.
+Interpreted languages such as Python and R translate instructions one at a
+time, while compiled languages like C and Fortran take whole documents, analyze
+the structure of the code, and perform optimizations before translating it to
+machine code.
-This leads to more efficient machine instructions of the compiled code at the cost of less flexibility and more verbose code.
-Most prominently the need for type declarations in compiled languages.
+This leads to more efficient machine instructions of the compiled code at the
+cost of less flexibility and more verbose code.
+Most prominently, compiled languages need an explicit type declaration for each
+variable.
-## Why julia?
+## Why Julia?
-Julia is a programming language that superficially looks like an interpreted language and mostly behaves like one.
-But before each function is executed it will compile it _just in time_.
+Julia is a programming language that superficially looks like an interpreted
+language and mostly behaves like one.
+But before each function is executed it will be compiled _just in time_.
-Thus you get the flexibility of an interpreted language and the execution speed of the compiled language at the cost of waiting a bit longer for the first execution of any function.
+Thus you get the flexibility of an interpreted language and the execution speed
+of a compiled language at the cost of waiting a bit longer for the first
+execution of any function.
-There is another aspect of julia that makes it interesting and that is the way packages compose.
-This is captured the best by an analogy from [Sam Urmy](https://github.com/ElOceanografo):
+There is another aspect of Julia that makes it interesting and that is the way
+packages compose.
+This is captured the best by an analogy from [Sam Urmy](
+https://github.com/ElOceanografo):
> Say you want a toy truck.
>
-> The Python/R solution is to look for the appropriate package–like buying a Playmobil truck. It comes pre-manufactured, well-engineered and tested, and does 95% of what you would ever want a toy truck to do.
+> The Python/R solution is to look for the appropriate package–like buying a
+> Playmobil truck. It comes pre-manufactured, well-engineered and tested, and
+> does 95% of what you would ever want a toy truck to do.
>
-> The Fortran/C solution is to build the truck from scratch. This allows total customization and you can optimize the features and performance however you want. The downside is that it takes more time, you need woodworking skills, and might hurt yourself with the power tools.
+> The Fortran/C solution is to build the truck from scratch. This allows total
+> customization and you can optimize the features and performance however you
+> want. The downside is that it takes more time, you need woodworking skills,
+> and might hurt yourself with the power tools.
>
-> The Julia solution is like Legos. You can get the truck kit if you want–which will require a bit more assembly than the Playmobil, but way less than building it from scratch. Or, you can get the component pieces and assemble the truck to your own specs. There’s no limit to what you can put together, and because the pieces all have the same system of bumps, everything snaps together quickly and easily.
+> The Julia solution is like Legos. You can get the truck kit if you want–which
+> will require a bit more assembly than the Playmobil, but way less than
+> building it from scratch. Or, you can get the component pieces and assemble
+> the truck to your own specs. There’s no limit to what you can put together,
+> and because the pieces all have the same system of bumps, everything snaps
+> together quickly and easily.
>
-> ![Trucks](https://aws1.discourse-cdn.com/business5/uploads/julialang/original/3X/5/2/52e63856ad9e23876cda4297a04171879fa625b4.jpeg){: height="400px"}
-> Ok, sure. Toy trucks are like linear algebra, though, a common request, and every “toy system” will have an implementation that works basically fine. But what if you want a time-traveling sailing/space ship with lasers AND dragon wings? And it should be as easy to build and use as a basic dump truck?
+> ![Trucks][vessel]{: height="400px"}
>
-> ![Vessel](https://aws1.discourse-cdn.com/business5/uploads/julialang/original/3X/2/8/2865d34fb35c181dc3c5c0f0b71915f31310269c.jpeg){: height="400px"}
-> There’s a reason that only Lego ever made anything like Dr Cyber’s Flying Time Vessel
+> OK, sure. Toy trucks are like linear algebra, though, a common request, and
+> every “toy system” will have an implementation that works basically fine. But
+> what if you want a time-traveling sailing/space ship with lasers AND dragon
+> wings? And it should be as easy to build and use as a basic dump truck?
+>
+> ![Vessel][vessel]{: height="400px"}
+>
+> There’s a reason that only Lego ever made anything like Dr. Cyber’s Flying
+> Time Vessel!
{: .quotation}
-Originally posted on [Discourse](https://discourse.julialang.org/t/what-is-the-advantage-of-julia-over-fortran/65964/101).
+Originally posted on [Discourse][discourse].
+
+[discourse]: https://discourse.julialang.org/t/what-is-the-advantage-of-julia-over-fortran/65964/101
+[truck]: https://aws1.discourse-cdn.com/business5/uploads/julialang/original/3X/5/2/52e63856ad9e23876cda4297a04171879fa625b4.jpeg
+[vessel]: https://aws1.discourse-cdn.com/business5/uploads/julialang/original/3X/2/8/2865d34fb35c181dc3c5c0f0b71915f31310269c.jpeg
{% include links.md %}
diff --git a/_episodes/02-REPL.md b/_episodes/02-REPL.md
index c4bec13d..507d1fde 100644
--- a/_episodes/02-REPL.md
+++ b/_episodes/02-REPL.md
@@ -10,9 +10,9 @@ objectives:
- "Learn about REPL modes."
keypoints:
- "The REPL reads the given input, evaluates the given expression and prints the resulting output to the user."
-- "Pressing ? enters help mode."
-- "Pressing ; enters shell mode."
-- "Pressing ] enters pkg mode."
+- "Pressing `?` enters help mode."
+- "Pressing `;` enters shell mode."
+- "Pressing `]` enters pkg mode."
---
@@ -20,9 +20,13 @@ keypoints:
## Variables
+After downloading and executing a Julia binary from
+[julialang.org](https://julialang.org), Melissa and her classmates face the so
+called REPL, which stands for **r**ead-**e**valuate-**p**rint-**l**oop. The
+interactive command-line REPL allows quick and easy execution of Julia
+statements.
+The first thing they try is to perform basic arithmetic operations:
-After downloading and executing a julia binary from https://julialang.org Melissa and her classmates face the so called REPL, which stands for read-evaluate-print-loop. The interactive command-line REPL allows quick and easy execution of Julia statements.
-The first thing they try is to perform basic arithmetic operations
~~~
1 + 4 * 7.3
~~~
@@ -33,7 +37,8 @@ The first thing they try is to perform basic arithmetic operations
{: .output}
That works as expected.
-It is also possible to create a variable by binding a name to a value via the assignment operator `=`, which makes it easier to refer to them later on.
+It is also possible to bind a name to a value via the assignment operator `=`,
+which makes it easier to refer to the value later on.
These names are called _variables_.
~~~
@@ -47,8 +52,9 @@ distance_x_2 = 2 * distance
{: .output}
Melissa notices that assignment also returns the value.
+She can also check which variables are defined in the current session by
+running
-She can also check which variables are defined in the current session by running
~~~
julia> varinfo()
name size summary
@@ -65,12 +71,15 @@ julia> varinfo()
## Unicode
-In julia unicode characters are also allowed as variables like `α = 2`.
-Unicode characters can be entered by a backslash followed by their LaTeX-name and then pressing tab (in this case `\alpha`tab).
+In Julia, Unicode characters are also allowed as variables like `α = 2`.
+Unicode characters can be entered by a backslash followed by their LaTeX-name
+and then pressing `tab` (in this case `\alpha``tab`).
## REPL-modes
-Unfortunately Melissa can't remember the LaTeX name of ∂ so she copies the character, presses ? to enter the help mode, pastes the character and gets
+Unfortunately Melissa can't remember the LaTeX name of ∂ so she copies the
+character, presses `?` to enter the help mode, pastes the character and gets
+
~~~
help?> ∂
"∂" can be typed by \partial
@@ -80,15 +89,18 @@ help?> ∂
Great! This way she can easily look up the names she needs.
She gets back to normal mode by pressing backspace.
-Another useful mode is the shell mode that can be entered by pressing ;.
+Another useful mode is the shell mode that can be entered by pressing `;`.
The prompt has now changed to shell.
-It can be used to issue commands of the underlying shell, but don't confuse it with an actual shell: Special shell syntax like piping won't work.
+It can be used to issue commands of the underlying shell, but don't confuse it
+with an actual shell: Special shell syntax like piping won't work.
> ## Hello shell mode
+>
> Use the shell mode to start nano and save your first `.jl` file
{: .challenge}
-Finally there is the package mode that is entered with ] which is used for package management, which will be covered later on.
-To exit the shell or pkg mode use backspace.
+Finally there is the package mode that is entered with `]` which is used for
+package management, which will be covered later on.
+To exit the shell or pkg mode use `backspace`.
{% include links.md %}
diff --git a/_episodes/03-types.md b/_episodes/03-types.md
index db0179f3..1f4dbcea 100644
--- a/_episodes/03-types.md
+++ b/_episodes/03-types.md
@@ -4,27 +4,33 @@ teaching: 15
exercises: 5
questions:
- "What is the use of types?"
-- "How are types organized in julia?"
+- "How are types organized in Julia?"
objectives:
- "Understand the structure of the type tree."
- "Know how to traverse the type tree."
- "Know how to build mutable and immutable types."
keypoints:
-- "In julia types have only one direct supertype."
+- "In Julia types have only one direct supertype."
---
## Structuring variables
-Melissa wants to keep the variables corresponding to the trebuchet (`counterweight`, `release_angle`) separate from the variables coming from the environment (`wind`, `target_distance`).
+Melissa wants to keep the variables corresponding to the trebuchet
+(`counterweight`, `release_angle`) separate from the variables coming from the
+environment (`wind`, `target_distance`).
That is why she chooses to group them together using _structures_.
-There are two type structures:
- - immutable structures, whose fields can not be changed after creation
- - keyword: `struct`
- - mutable structures, whose fields can change after creation
- - keyword: `mutable struct`
-
-Since Melissa wants to change the parameters of the trebuchet and uses a `mutable struct` for this.
-But she cannot influence the environment and thus uses a `struct` for it.
+There are two structure types:
+
+- immutable structures, whose fields can not be changed after creation
+ - keyword: `struct`
+- mutable structures, whose fields can change after creation
+ - keyword: `mutable struct`
+
+Since Melissa wants to change the parameters of the trebuchet, she uses a
+`mutable struct` for it.
+But she cannot influence the environment and thus uses a `struct` for those
+values.
+
~~~
mutable struct Trebuchet
counterweight::Float64
@@ -40,10 +46,13 @@ end
### Types and hierarchy
-Here `::Float64` is a type specification, indicating that this variable should be a 64-bit floating point number.
-If Melissa hadn't specified the type, the variables would have the type `Any` by default.
+Here `::Float64` is a type specification, indicating that this variable should
+be a 64-bit floating point number.
+If Melissa hadn't specified the type, the variables would have the type `Any`
+by default.
-In julia every type can have only one supertype, so lets check how many types are between `Float64` and `Any`
+In Julia every type can have only one supertype, so lets check how many types
+are between `Float64` and `Any`
~~~
julia> supertype(Float64)
@@ -57,21 +66,27 @@ Any
~~~
{: .language-julia}
-So we have the relationship `Float64 <: AbstractFloat <: Real <: Number <: Any`, where `<:` means "subtype of".
+So we have the relationship `Float64 <: AbstractFloat <: Real <: Number <:
+Any`, where `<:` means "subtype of".
-`Float64` is a _concrete_ type, which means that you can actually create objects of this type.
+`Float64` is a _concrete_ type, which means that you can actually create
+objects of this type.
For example `1.0` is a object of type `Float64`.
We can check this at the REPL:
+
~~~
julia> 1.0 isa Float64
true
~~~
{: .language-julia}
-All the other types are _abstract_ types that are used to address groups of types.
-For example, if we declare a variable as `a::Real` then it can be bound to any value that is a subtype of `Real`.
+All the other types are _abstract_ types that are used to address groups of
+types.
+For example, if we declare a variable as `a::Real` then it can be bound to any
+value that is a subtype of `Real`.
Let's quickly check what are all the subtypes of `Real`:
+
~~~
julia> subtypes(Real)
4-element Array{Any,1}:
@@ -82,13 +97,14 @@ julia> subtypes(Real)
~~~
{: .language-julia}
-This way the types form a tree with abstract types on the nodes and concrete types as leafs.
+This way the types form a tree with abstract types on the nodes and concrete
+types as leaves.
Have a look at this visualization of all subtypes of `Number`:
![Type_tree-Number](https://upload.wikimedia.org/wikipedia/commons/thumb/4/40/Type-hierarchy-for-julia-numbers.png/1200px-Type-hierarchy-for-julia-numbers.png)
> ## Is it `Real`?
>
-> For which of these types `T` does not hold `(1.0 isa T) == true`?
+> For which of these types `T` is it false that `(1.0 isa T)`?
>
> 1. Real
> 2. Number
@@ -96,15 +112,20 @@ Have a look at this visualization of all subtypes of `Number`:
> 4. Integer
>
> > ## Solution
-> > The correct answer is 4. While `1.0` represents an integer value it is still a floating point number in contrast to `1`.
->{: .solution}
+> >
+> > The correct answer is 4:
+> > while `1` is an integer, `1.0` is its floating-point representation.
+> {: .solution}
{: .challenge}
## Creating a subtype
-A concrete type can be made a subtype of an abstract type with the subtype operator `<:`.
-Since `Trebuchet` contains several fields that are mutable Melissa thinks it is a good idea to make it a subtype of `AbstractVector`.
+A concrete type can be made a subtype of an abstract type with the subtype
+operator `<:`.
+Since `Trebuchet` contains several fields that are mutable Melissa thinks it is
+a good idea to make it a subtype of `AbstractVector`.
+
~~~
julia> mutable struct Trebuchet <: AbstractVector{Float64}
counterweight::Float64
@@ -122,9 +143,10 @@ Stacktrace:
{: .error}
> ## Caveat: redefining `struct`s
-> In julia it is not very easy to redefine `struct`s.
-> It is necessary to restart the REPL to define the new definition of `Trebuchet`
-> or take a different name.
+>
+> In Julia it is not very easy to redefine `struct`s.
+> It is necessary to restart the REPL to define the new definition of
+> `Trebuchet` or take a different name.
{: .callout}
Melissa decides to keep going and come back to this later.
diff --git a/_episodes/04-pkg.md b/_episodes/04-pkg.md
index 345a023d..2d9cdb24 100644
--- a/_episodes/04-pkg.md
+++ b/_episodes/04-pkg.md
@@ -18,30 +18,40 @@ keypoints:
## The package manager
-Now it is time for Melissa and their mates to simulate the launch of the trebuchet.
-The necessary equations are really complicated, but an investigation on [juliahub](https://juliahub.com/) revealed that someone already implemented these and published it as the julia package `Trebuchet.jl`.
+Now it is time for Melissa and their mates to simulate the launch of the
+trebuchet.
+The necessary equations are really complicated, but an investigation on
+[juliahub](https://juliahub.com/) revealed that someone already implemented
+these and published it as the Julia package `Trebuchet.jl`.
That spares some real work.
-Melissa enters the package mode by pressing ].
-The `julia>` prompt becomes a blue prompt that reads the julia version that Melissa is running.
-After consulting the [documentation](https://julialang.github.io/Pkg.jl/v1/) she knows that the prompt is showing the currently activated environment and that this is the global environment that is activated by default.
+Melissa enters the package mode by pressing `]`.
+The `julia>` prompt becomes a blue prompt that reads the Julia version that
+Melissa is running.
+After consulting the [documentation](https://julialang.github.io/Pkg.jl/v1/)
+she knows that the prompt is showing the currently activated environment and
+that this is the global environment that is activated by default.
+
+However, she doesn't want to clutter the global environment when working on her
+project, so she creates a new environment via
-However, she doesn't want to clutter the global environment when working on her project, so she creates a new environment via
~~~
(v1.x) pkg> activate projects/trebuchet
~~~
{: .language-julia}
In this environment she adds the `Trebuchet` package by typing
+
~~~
(trebuchet) pkg> add Trebuchet
~~~
{: .language-julia}
-Melissa quickly recognizes that far more packages are being installed than just `Trebuchet`.
+Melissa quickly recognizes that far more packages are being installed than just
+`Trebuchet`.
These are the dependencies of `Trebuchet`.
-
From the output
+
~~~
[...]
Updating `[...]/projects/trebuchet/Project.toml`
@@ -56,10 +66,16 @@ Updating `[...]/projects/trebuchet/Project.toml`
she sees that two files were created: `Project.toml` and `Manifest.toml`.
-The project file `Project.toml` only contains the packages needed for her project, while the manifest file `Manifest.toml` records the direct and indirect dependencies as well as their current version, thus providing a fully reproducible record of the code that is actually executed.
-"That is really handy when I want to share my work with the others." thinks Melissa.
+The project file `Project.toml` only contains the packages needed for her
+project, while the manifest file `Manifest.toml` records the direct and
+indirect dependencies as well as their current version, thus providing a fully
+reproducible record of the code that is actually executed.
+"That is really handy when I want to share my work with the others," thinks
+Melissa.
+
+After the installation finished she can check the packages present in her
+environment.
-After the installation finished she can check the packages present in her environment.
~~~
(trebuchet) pkg> status
~~~
@@ -68,18 +84,22 @@ After the installation finished she can check the packages present in her enviro
## Using and importing packages
Now that Melissa added the package to her environment, she needs to load it.
-Julia provides two keywords for loading packages `using` and `import`.
+Julia provides two keywords for loading packages: `using` and `import`.
-The difference is that `import` brings only the name of the package into the namespace and then all functions in that package needs the name in front.
-But packages can also define an export list for function names that should be brought into the users namespace when he loads the package with `using`.
-This makes working at the REPL often more convenient.
+The difference is that `import` brings only the name of the package into the
+namespace and then all functions in that package need the name in front.
+But packages can also define an export list for function names that should be
+brought into the user's namespace when he loads the package with `using`.
+This makes working at the REPL more convenient.
### Name conflicts
It may happen that name conflicts arise.
-For example defined Melissa a structure named `Trebuchet`, but the package she added to the environment is also named `Trebuchet`.
+For example Melissa defined a structure named `Trebuchet`, but the package she
+added to the environment is also named `Trebuchet`.
Now she would get an error if she tried to `import`/`using` it directly.
-One solution is to rename the package upon `import` with `as`.
+One solution is to rename the package upon `import` with `as`:
+
~~~
import Trebuchet as Trebuchets
~~~
diff --git a/_episodes/05-functions.md b/_episodes/05-functions.md
index 0f4ba1b0..d945d509 100644
--- a/_episodes/05-functions.md
+++ b/_episodes/05-functions.md
@@ -13,41 +13,54 @@ objectives:
keypoints:
- "You can think of functions being a collection of methods"
- "Keep the number of positional arguments low"
-- "Macros transform julia expressions"
+- "Macros transform Julia expressions"
---
## Working with functions
-Now that Melissa successfully installed the package she wants to figure out what she can do with it.
+Now that Melissa successfully installed the package she wants to figure out
+what she can do with it.
-Julias `Base` module offers a handy function for inspecting other modules called `names`.
+Julia's `Base` module offers a handy function for inspecting other modules
+called `names`.
Let's look at its docstring:
+
~~~
help?> names
names(x::Module; all::Bool = false, imported::Bool = false)
- Get an array of the names exported by a Module, excluding deprecated names. If all is true, then the list also includes non-exported names defined in the module, deprecated names, and compiler-generated names. If imported
- is true, then names explicitly imported from other modules are also included.
+ Get an array of the names exported by a Module, excluding deprecated names.
+ If all is true, then the list also includes non-exported names defined in
+ the module, deprecated names, and compiler-generated names. If imported is
+ true, then names explicitly imported from other modules are also included.
- As a special case, all names defined in Main are considered "exported", since it is not idiomatic to explicitly export names from Main.
+ As a special case, all names defined in Main are considered "exported",
+ since it is not idiomatic to explicitly export names from Main.
~~~
{: .language-julia}
+
> ## Positional and keyword arguments
+>
> Let's take a closer look at the signature of the `names` function.
-> In julia we have two types of arguments:
-> 1. _Positional arguments_ are determined by their position and thus the order in which arguments are given to the function matters.
-> The `names` function has one positional argument `x` of type `Module`.
-> 2. _Keyword arguments_ are passed as a combination of the keyword and the value to the function.
-> They can be given in any order, but they need to have a default value.
-> The `names` function has two keyword arguments `all` and `imported` which are both of type `Bool` and default to `false`.
-> Positional and keyword arguments are separated by a semi-colon.
+> In Julia we have two types of arguments:
+>
+> 1. _Positional arguments_ are determined by their position and thus the order
+> in which arguments are given to the function matters. The `names` function
+> has one positional argument `x` of type `Module`.
+> 2. _Keyword arguments_ are passed as a combination of the keyword and the
+> value to the function. They can be given in any order, but they need to
+> have a default value. The `names` function has two keyword arguments
+> (`all` and `imported`) which are both of type `Bool` and default to
+> `false`.
>
+> Positional and keyword arguments are separated by a semi-colon.
{: .callout}
> ## Calling with keyword arguments
>
-> Suppose Melissa wanted to get `all` names of the `Trebuchets` module, what would the call look like?
+> Suppose Melissa wanted to get `all` names of the `Trebuchets` module, what
+> would the call look like?
>
> 1. `names(Trebuchets, true)`
> 2. `names(Trebuchets, all = true)`
@@ -55,13 +68,14 @@ help?> names
> 4. `names(Trebuchets, all)`
> 5. Answer 2. and 3.
>
-> >## Solution
-> > its 5
+> > ## Solution
+> >
+> > Option 5 is correct.
> {: .solution}
{: .challenge}
-
Thus Melissa executes
+
~~~
julia> names(Trebuchets)
6-element Vector{Symbol}:
@@ -75,44 +89,71 @@ julia> names(Trebuchets)
{: .language-julia}
which yields the exported names of the `Trebuchets` module.
-By convention types are named with _CamelCase_ while functions typically have _snake_case_.
-Since Melissa is interested in simulating shots, she looks at the `Trebuchets.shoot` function
+By convention types are named with *CamelCase* while functions typically have
+*snake_case*.
+Since Melissa is interested in simulating shots, she looks at the
+`Trebuchets.shoot` function
+
~~~
help?> Trebuchets.shoot
shoot(ws, angle, w)
shoot((ws, angle, w))
- Shoots a Trebuchet with weight w. Releases the weight at the release angle angle in radians. The current wind speed is ws. Returns (t, dist), with travel time t and traveled distance dist.
+ Shoots a Trebuchet with weight w. Releases the weight at the release angle
+ angle in radians. The current wind speed is ws. Returns (t, dist), with
+ travel time t and traveled distance dist.
~~~
{: .language-julia}
+
> ## Methods
-> Here we see, that the `shoot` function has two different _methods_.
-> The first one takes three arguments, while the second takes a `Tuple` with three elements.
+>
+> Here we see that the `shoot` function has two different _methods_.
+> The first one takes three arguments, while the second takes a `Tuple` with
+> three elements.
{: .callout}
-Now she is ready to fire the first shot
+Now she is ready to fire the first shot.
+
~~~
julia> Trebuchets.shoot(5, 0.25pi, 500)
-(TrebuchetState(Trebuchet.Lengths{Float64}(1.524, 2.0702016, 0.5334, 0.6096, 2.0826984, 0.8311896, 0.037947600000000005), Trebuchet.Masses{Float64}(226.796185, 0.14877829736, 4.8307587405), Trebuchet.Angles{Float64}(-0.5033953025972455, 1.322643200282786, 1.4614900249109948), Trebuchet.AnglularVelocities{Float64}(-5.571655186015145, 7.720538762011071, -25.384361188794127), Trebuchet.Constants{Float64}(5.0, 1.0, 1.0, 9.80665, 0.7853981633974482), Trebuchet.Inertias{Float64}(0.042140110093804806, 2.7288719786342384), Val{:End}(), 60.0, Trebuchet.Vec(117.8468674726198, -1.5239999999999974), Trebuchet.Vec(10.790333612654146, -21.45379494231241), Solution(394)
-, 0, Val{:Released}()), 117.8468674726198)
+(TrebuchetState(Trebuchet.Lengths{Float64}(1.52, 2.07, 0.533, 0.607, 2.08, 0.831, 0.0379),
+ Trebuchet.Masses{Float64}(226.0, 0.149, 4.83),
+ Trebuchet.Angles{Float64}(-0.503, 1.32, 1.46),
+ Trebuchet.AnglularVelocities{Float64}(-5.57, 7.72, -25.4),
+ Trebuchet.Constants{Float64}(5.0, 1.0, 1.0, 9.81, 0.785),
+ Trebuchet.Inertias{Float64}(0.042, 2.73),
+ Val{:End}(),
+ 60.0,
+ Trebuchet.Vec(117.8, -1.524),
+ Trebuchet.Vec(10.79, -21.45),
+ Solution(394),
+ 0,
+ Val{:Released}()
+ ),
+ 117.8
+)
~~~
{: .language-julia}
-That is a lot of output, but Melissa is actually only interested in the distance, which is the second element of the tuple that was returned.
-So she tries again and grabs the second element this time
+That is a lot of output, but Melissa is actually only interested in the
+distance, which is the second element of the tuple that was returned.
+So she tries again and grabs the second element this time:
+
~~~
julia> Trebuchets.shoot(5, 0.25pi, 500)[2]
-117.8468674726198
+117.8
~~~
{: .language-julia}
-which means the shot traveled approximately 118m.
+which means the shot traveled approximately 118 m.
### Defining functions
-Melissa wants to make her future work easier and she fears she might forget to take the second element.
+Melissa wants to make her future work easier and she fears she might forget to
+take the second element.
That's why she puts it together in a _function_ like this:
+
~~~
julia> function shoot_distance(windspeed, angle, weight)
Trebuchets.shoot(windspeed, angle, weight)[2]
@@ -121,13 +162,18 @@ julia> function shoot_distance(windspeed, angle, weight)
{: .language-julia}
> ## Implicit return
-> Note that Melissa didn't have to use the `return` keyword, since in julia the value of the last line will be returned by default.
-> But she could have used an explicit return and the function would behave the same.
+>
+> Note that Melissa didn't have to use the `return` keyword, since in Julia the
+> value of the last line will be returned by default.
+> But she could have used an explicit return and the function would behave the
+> same.
{: .callout}
### Adding methods
-Since Melissa wants to work with the structs `Trebuchet` and `Environment` she adds another convenience method for those
+Since Melissa wants to work with the structs `Trebuchet` and `Environment` she
+adds another convenience method for those
+
~~~
julia> function shoot_distance(trebuchet::Trebuchet, env::Environment)
shoot_distance(env.wind, trebuchet.release_angle, trebuchet.counterweight)
@@ -135,12 +181,16 @@ julia> function shoot_distance(trebuchet::Trebuchet, env::Environment)
~~~
{: .language-julia}
-This method will call the former method and pass the correct fields from the `Trebuchet` and `Environment` structures.
+This method will call the former method and pass the correct fields from the
+`Trebuchet` and `Environment` structures.
### Slurping and splatting
-By peeking into the [documentation](https://docs.julialang.org/en/v1/manual/faq/#The-two-uses-of-the-...-operator:-slurping-and-splatting) Melissa discovers that she doesn't need to explicitly declare all the input arguments.
-Instead she can _slurp_ the arguments in the function definition and _splat_ them in the function body using three dots (`...`) like this
+By peeking into the [documentation][slurp], Melissa discovers that she
+doesn't need to explicitly declare all the input arguments.
+Instead she can _slurp_ the arguments in the function definition and _splat_
+them in the function body using three dots (`...`) like this:
+
~~~
julia> function shoot_distance(args...) # slurping
Trebuchets.shoot(args...)[2] # splatting
@@ -151,16 +201,17 @@ julia> function shoot_distance(args...) # slurping
### Anonymous functions
-Sometimes it is useful to have a new function and not having to come up with a new name.
+Sometimes it is useful to have a new function and not have to come up with a
+new name.
These are _anonymous functions_.
-They can be defined by either with the so called stabby lambda notation
+They can be defined with either the so-called stabby lambda notation,
~~~
julia> (windspeed, angle, weight) -> Trebuchets.shoot(windspeed, angle, weight)[2]
~~~
{: .language-julia}
-or in the long form, by omitting the name:
+or in long form, by omitting the name:
~~~
julia> function (windspeed, angle, weight)
@@ -173,6 +224,7 @@ julia> function (windspeed, angle, weight)
Melissa would like to set the fields of a `Trebuchet` using an index.
She writes
+
~~~
julia> trebuchet[1] = 2
ERROR: MethodError: no method matching setindex!(::Trebuchet, ::Int64, ::Int64)
@@ -185,10 +237,13 @@ Stacktrace:
which tells her two things:
1. a function named `setindex!` was called
-2. it didn't have a method for `Trebuchet`s
+2. it didn't have a method for `Trebuchet`
+
+Melissa wants to add the missing method to `setindex!` but she doesn't know
+where it is defined.
+There is a handy _macro_ named `@which` that obtains the module where the
+function is defined.
-Melissa wants to add the missing method to `setindex!` but she doesn't know where it is defined.
-There is a handy _macro_ named `@which` which can be used to obtain the module where the function is defined.
~~~
julia> @which setindex!
Base
@@ -196,11 +251,16 @@ Base
{: .language-julia}
> ## Macros
-> Macro names begin with `@` and they don't need parentheses or commas to delimit their arguments.
-> Macros can transform any valid julia expression and are quite powerful.
+>
+> Macro names begin with `@` and they don't need parentheses or commas to
+> delimit their arguments.
+> Macros can transform any valid Julia expression and are quite powerful.
> They can be expanded using `@macroexpand`.
{: .callout}
-Now Melissa knows she needs to add a method to `Base.setindex!` with the signature `(::Trebuchet, ::Int64, ::Int64)`.
+Now Melissa knows she needs to add a method to `Base.setindex!` with the
+signature `(::Trebuchet, ::Int64, ::Int64)`.
+
+[slurp]: https://docs.julialang.org/en/v1/manual/faq/#The-two-uses-of-the-...-operator:-slurping-and-splatting
{% include links.md %}
diff --git a/_episodes/06-control-flow.md b/_episodes/06-control-flow.md
index f17d3ebb..1c3170fc 100644
--- a/_episodes/06-control-flow.md
+++ b/_episodes/06-control-flow.md
@@ -19,11 +19,13 @@ keypoints:
Now that Melissa knows which method to add she thinks about the implementation.
-If the index is `1` she wants to set `counterweight` while if the index is `2` she wants to set `release_angle` and since these are the only to fields she wants to return an error if anything else comes in.
-In julia the keywords to specify conditions are `if`, `elseif` and `else`.
-Closed with an `end`.
-
+If the index is `1` she wants to set `counterweight` while if the index is `2`
+she wants to set `release_angle` and since these are the only two fields she
+wants to return an error if anything else comes in.
+In Julia the keywords to specify conditions are `if`, `elseif` and `else`,
+closed with an `end`.
Thus she writes
+
~~~
function Base.setindex!(trebuchet::Trebuchet, v, i::Int)
if i === 1
@@ -39,85 +41,109 @@ end
### Interfaces
-`setindex!` is actually one function of a widespread _interface_ in the julia language: `AbstractArray`s.
-An interface is a collection of methods that are all implemented by a certain type.
-For example lists the [julia manual](https://docs.julialang.org/en/v1/manual/interfaces/#man-interface-array) all methods that a subtype of `AbstractArray` need to implement to adhere to the `AbstractArray` interface.
-If Melissa does this then her `Trebuchet` type will work with every function in `Base` that accepts an `AbstractArray`.
-
-She also needs to make `Trebuchet` a proper subtype of `AbstractArray` as she tried in [the types episode]({{ site.baseurl }}{%link _episodes/03-types.md %}).
+`setindex!` is actually one function of a widespread _interface_ in the Julia
+language: `AbstractArray`s.
+An interface is a collection of methods that are all implemented by a certain
+type.
+For example, the [Julia
+manual](https://docs.julialang.org/en/v1/manual/interfaces/#man-interface-array)
+lists all methods that a subtype of `AbstractArray` need to implement to adhere
+to the `AbstractArray` interface.
+If Melissa does this then her `Trebuchet` type will work with every function in
+`Base` that accepts an `AbstractArray`.
+
+She also needs to make `Trebuchet` a proper subtype of `AbstractArray` as she
+tried in [the types episode]({{ site.baseurl }}{%link _episodes/03-types.md
+%}).
Therefore she restarts her REPL.
> ## Implement the `AbstractArray` interface for `Trebuchet`
+>
> Now we know enough to actually implement the `AbstractArray` interface.
> You don't need to implement the optional methods.
>
> Hint: Take a look at the docstrings of `getfield` and `tuple`.
>
->> ## Solution
->> ~~~
->> Base.size(trebuchet::Trebuchet) = tuple(2)
->> Base.getindex(trebuchet::Trebuchet, i::Int) = getfield(trebuchet, i)
->> function Base.setindex!(trebuchet::Trebuchet, v, i::Int)
->> if i === 1
->> trebuchet.counterweight = v
->> elseif i === 2
->> trebuchet.release_angle = v
->> else
->> error("Trebuchet only accepts indices 1 and 2, yours is $i")
->> end
->> end
->> ~~~
->>{: .language-julia}
->{: .solution}
+> > ## Solution
+> >
+> > ~~~
+> > Base.size(trebuchet::Trebuchet) = tuple(2)
+> > Base.getindex(trebuchet::Trebuchet, i::Int) = getfield(trebuchet, i)
+> > function Base.setindex!(trebuchet::Trebuchet, v, i::Int)
+> > if i === 1
+> > trebuchet.counterweight = v
+> > elseif i === 2
+> > trebuchet.release_angle = v
+> > else
+> > error("Trebuchet only accepts indices 1 and 2, yours is $i")
+> > end
+> > end
+> > ~~~
+> > {: .language-julia}
+> {: .solution}
{: .challenge}
## Loops
-Now Melissa knows how to shoot the virtual trebuchet and get the distance of the projectile, but in order to aim she needs to make a lot of trial shots in a row.
+Now Melissa knows how to shoot the virtual trebuchet and get the distance of
+the projectile, but in order to aim she needs to take a lot of trial shots in a
+row.
She wants her trebuchet to only shoot a hundred meters.
-She could execute the function several times on the REPL with different parameters, but that gets tiresome quickly.
+She could execute the function several times on the REPL with different
+parameters, but that gets tiresome quickly.
A better way to do this is to use loops.
But first Melissa needs a way to improve her parameters.
-> ## Digression: gradients
-> The `shoot_distance` function takes three input parameters and returns one value (the distance).
-> Whenever we change one of the input parameters, we will get a different distance.
+> ## Digression: Gradients
>
-> The [_gradient_](https://en.wikipedia.org/wiki/Gradient) of a function gives the direction in which the return value will change by the largest amount.
+> The `shoot_distance` function takes three input parameters and returns one
+> value (the distance).
+> Whenever we change one of the input parameters, we will get a different
+> distance.
>
-> Since the `shoot_distance` function has three input parameters, the gradient of `shoot_distance` will return a 3-element `Array`.
-> One direction for each input parameter.
+> The [_gradient_][grad] of a function gives the direction in which the return
+> value will change by the largest amount.
>
-> Thanks to [automatic differentiation](https://en.wikipedia.org/wiki/Automatic_differentiation) and the julia package `ForwardDiff.jl` gradients can be calculated easily.
-{: .quotation}
+> Since the `shoot_distance` function has three input parameters, the gradient
+> of `shoot_distance` will return a 3-element `Array`:
+> one direction for each input parameter.
+>
+> Thanks to [automatic differentiation][autodiff] and the Julia package
+> `ForwardDiff.jl` gradients can be calculated easily.
+{: .callout}
-Melissa uses the `gradient` function of `ForwardDiff.jl` to get the direction in which she needs to change the parameters to make the largest difference.
+Melissa uses the `gradient` function of `ForwardDiff.jl` to get the direction
+in which she needs to change the parameters to make the largest difference.
> ## Do you remember?
-> What does Melissa need to write into the REPL to install the package `ForwardDiff`?
+>
+> What does Melissa need to write into the REPL to install the package
+> `ForwardDiff`?
>
> 1. `] install ForwardDiff`
> 2. `add ForwardDiff`
> 3. `] add ForwardDiff.jl`
> 4. `] add ForwardDiff`
>
->> ## Solution
->> The correct solution is 4.
->> ] to enter Pkg-mode.
->> Then
->> ~~~
->> pkg> add ForwardDiff
->> ~~~
->> {: .language-julia}
->{: .solution}
+> > ## Solution
+> >
+> > The correct solution is 4:
+> > `]` to enter pkg mode, then
+> >
+> > ~~~
+> > pkg> add ForwardDiff
+> > ~~~
+> > {: .language-julia}
+> {: .solution}
{: .challenge}
-
-
+
+
~~~
julia> using ForwardDiff: gradient
@@ -131,9 +157,11 @@ julia> grad = gradient(x -> (shoot_distance([environment.wind, x[2], x[1]] - env
~~~
{: .language-julia}
-Melissa now changes her arguments a little bit in the direction of the gradient and checks the new distance.
+Melissa now changes her arguments a little bit in the direction of the gradient
+and checks the new distance.
+
~~~
julia> better_trebuchet = imprecise_trebuchet - 0.05 * grad;
@@ -145,13 +173,18 @@ julia> shoot_distance([5, new_arguments...])
That got shorter, but also a bit too short.
> ## Experiment
-> How far can you change the parameters in the direction of the gradient, such that it still improves the distance?
+>
+> How far can you change the parameters in the direction of the gradient, such
+> that it still improves the distance?
{: .discussion}
### For loops
-Now that Melissa knows it is going in the right direction she wants to automate the additional iterations.
-She writes a new function `aim`, that performs the application of the gradient `N` times.
+Now that Melissa knows it is going in the right direction she wants to automate
+the additional iterations.
+She writes a new function `aim`, that performs the application of the gradient
+`N` times.
+
~~~
julia> function aim(trebuchet, environment; N = 10, η = 0.05)
better_trebuchet = copy(trebuchet)
@@ -171,24 +204,31 @@ julia> shoot_distance(better_trebuchet, environment)
{: .language-julia}
> ## Explore
+>
> Play around with different inputs of `N` and `η`.
> How close can you come?
+>
> > ## Reason
+> >
> > This is a highly non-linear system and thus very sensitive.
-> > The distances across different values for the counterweight and the release angle α look like this
+> > The distances across different values for the counterweight and the release
+> > angle α look like this:
+> >
> > ![distance-surface](../fig/shoot_surface.png)
> {: .solution}
{: .discussion}
> ## Aborting programs
-> If a call takes too long, you can abort it with ctrl-c
+> If a call takes too long, you can abort it with `Ctrl-c`
{: .callout}
### While loops
-Melissa finds the output of the above `aim` function too unpredictable to be useful.
+Melissa finds the output of the above `aim` function too unpredictable to be
+useful.
That's why she decides to change it a bit.
-This time she uses a `while`-loop to run the iterations until she is sufficiently near her target.
+This time she uses a `while`-loop to run the iterations until she is
+sufficiently near her target.
~~~
julia> function aim(trebuchet::Trebuchet, environment::Environment; ε = 1e-1, η = 0.05)
@@ -212,3 +252,8 @@ julia> shoot_distance(better_trebuchet, environment)
{: .language-julia}
That is more what she had in mind.
+
+[autodiff]: https://en.wikipedia.org/wiki/Automatic_differentiation
+[grad]: https://en.wikipedia.org/wiki/Gradient
+
+{% include links.md %}
diff --git a/_episodes/07-modules.md b/_episodes/07-modules.md
index 3ec8f547..c6c542c3 100644
--- a/_episodes/07-modules.md
+++ b/_episodes/07-modules.md
@@ -1,5 +1,5 @@
---
-title: "using Modules"
+title: "Using Modules"
teaching: 15
exercises: 0
questions:
@@ -9,17 +9,21 @@ objectives:
- "Use Revise.jl to track changes"
keypoints:
- "Modules introduce namespaces"
-- "Public API has to be documented and can be exported."
+- "Public API has to be documented and can be exported"
---
## Modules
-Melissa now has a bunch of definitions in her running julia session and using the REPL for interactive exploration is great, but on the one hand it is more and more taxing to keep in mind, what is defined and on the other hand all the definitions are lost once she closes the REPL.
+Melissa now has a bunch of definitions in her running Julia session and using
+the REPL for interactive exploration is great, but it is more and more taxing
+to keep in mind what is defined, and all the definitions are lost once she
+closes the REPL.
That is why she decides to put her code in a file.
-She opens up her text editor and creates a file called `aim_trebuchet.jl` in the current working directory and pastes the code she got so far in there.
-
+She opens up her text editor and creates a file called `aim_trebuchet.jl` in
+the current working directory and pastes the code she got so far in there.
This is what it looks like:
+
~~~
import Trebuchet as Trebuchets
using ForwardDiff: gradient
@@ -70,21 +74,30 @@ imprecise_trebuchet = Trebuchet(500.0, 0.25pi)
environment = Environment(5, 100)
precise_trebuchet = aim(imprecise_trebuchet, environment)
shoot_distance(precise_trebuchet, environment)
-
~~~
-{: .language-julia}
+{: .language-julia title="aim_trebuchet.jl"}
Now Melissa can run `include(aim_trebuchet.jl)` in the REPL to execute her code.
-She also recognizes that she has a bunch of definitions at the beginning that she doesn't need to execute more than once in a session and some lines at the end that use these definitions which she might run more often.
-She will split these in two separate files and put the definitions into a _module_.
-The module will put the definitions into their own namespace which is the module name.
-This means Melissa would need to put the module name before each definition if she uses it outside of the module.
-But she remembers from the [Pkg episode]({{ page.root }}{% link _episodes/04-pkg.md %}) that she can export names that don't need to be prefixed.
-
-She names her module `MelissasModule` and accordingly the file `MelissasModule.jl`.
-From this module she exports the names `aim`, `shoot_distance`, `Trebuchet` and `Environment`.
+She also recognizes that she has a bunch of definitions at the beginning that
+she doesn't need to execute more than once in a session and some lines at the
+end that use these definitions which she might run more often.
+She will split these in two separate files and put the definitions into a
+_module_.
+The module will put the definitions into their own namespace which is the
+module name.
+This means Melissa would need to put the module name before each definition if
+she uses it outside of the module.
+But she remembers from the [pkg episode](
+{{ page.root }}{% link _episodes/04-pkg.md %}) that she can export names that
+don't need to be prefixed.
+
+She names her module `MelissasModule` and accordingly the file
+`MelissasModule.jl`.
+From this module she exports the names `aim`, `shoot_distance`, `Trebuchet` and
+`Environment`.
This way she can leave her other code unchanged.
+
~~~
module MelissasModule
import Trebuchet as Trebuchets
@@ -134,11 +147,11 @@ function aim(trebuchet::Trebuchet, environment::Environment; ε = 1e-1, η = 0.0
return Trebuchet(better_trebuchet[1], better_trebuchet[2])
end
end # MelissasModule
-
~~~
-{: .language-julia}
+{: .language-julia title="MelissasModule.jl"}
The rest of the code goes to a file she calls `MelissasCode.jl`.
+
~~~
using .MelissasModule
@@ -147,21 +160,28 @@ environment = Environment(5, 100)
precise_trebuchet = aim(imprecise_trebuchet, environment)
shoot_distance(precise_trebuchet, environment)
~~~
-{: .language-julia}
+{: .language-julia title="MelissasCode.jl"}
-Now she can include `MelissasModule.jl` once, and change and include `MelissasCode.jl` as often as she wants.
+Now she can include `MelissasModule.jl` once, and change and include
+`MelissasCode.jl` as often as she wants.
But what if she wants to make changes to the module?
-If she changes the code in the module, reincludes the module and runs her code again, she only gets a bunch of warnings, but her changes are not applied.
+If she changes the code in the module, re-includes the module and runs her code
+again, she only gets a bunch of warnings, but her changes are not applied.
## Revise.jl
-`Revise.jl` is a package that can keep track of changes in your files and load these in a running julia session.
+`Revise.jl` is a package that can keep track of changes in your files and load
+these in a running Julia session.
+
+Melissa needs to take two things into account:
-Melissa needs to take two things into account
- - `using Revise` must come before `using` any Package that she wants to be tracked
- - she should use `includet` instead of `include` for included files (`t` for "tracking")
+- `using Revise` must come before `using` any Package that she wants to be
+ tracked
+- she should use `includet` instead of `include` for included files (`t` for
+ "tracking")
Thus she now runs
+
~~~
julia> using Revise
@@ -172,9 +192,11 @@ julia> include("MelissasCode.jl")
~~~
{: .language-julia}
-and any change she makes in `MelissasModule.jl` will be visible in the next run of her code.
+and any change she makes in `MelissasModule.jl` will be visible in the next run
+of her code.
> ## Did I say any changes?
+>
> Well, almost any. Revise can't track changes to structures.
{: .callout}
diff --git a/_episodes/08-packages.md b/_episodes/08-packages.md
index 655ed754..b52235e9 100644
--- a/_episodes/08-packages.md
+++ b/_episodes/08-packages.md
@@ -7,25 +7,31 @@ questions:
objectives:
- "Learn setting up a project using modules."
- "Learn common package structure."
-- "Learn to browse github or juliahub for packages and find documentation."
+- "Learn to browse GitHub or juliahub for packages and find documentation."
keypoints:
-- "The general registry is hosted on github."
+- "The general registry is hosted on GitHub."
- "Packaging is easy"
---
-Melissa is now confident that her module fine and she wants to make it available for the rest of her physics club.
+Melissa is now confident that her module fine and she wants to make it
+available for the rest of her physics club.
She decides to put it in a package.
-This way she can also locally use julias package manager for managing her module.
+This way she can also locally use Julia's package manager for managing her
+module.
## From project to package
-The path from having a module to having a package is actually very short: Packages need a `name` and a `uuid` field in their `Project.toml`.
+The path from having a module to having a package is actually very short:
+Packages need a `name` and a `uuid` field in their `Project.toml`.
-A `uuid` is a universally unique identifier.
-Thankfully julia comes with the `UUIDs` package, that can generate `uuid`s for Melissa via `UUIDs.uuid4()`.
+A UUID is a **u**niversally **u**nique **id**entifier.
+Thankfully Julia comes with the `UUIDs` package, that can generate `uuid`s for
+Melissa via `UUIDs.uuid4()`.
In addition Melissa needs to have a specific directory structure.
-She looks at the example package [`Example.jl`](https://github.com/JuliaLang/Example.jl) which has the following structure
+She looks at the example package [`Example.jl`][ex] which has the following
+structure:
+
~~~
├── docs
│ ├── make.jl
@@ -43,30 +49,46 @@ She looks at the example package [`Example.jl`](https://github.com/JuliaLang/Exa
{: .output}
> ## Make it a package
-> Open your `Project.toml` and add `name = `, `uuid = ` and optionally an `authors` field.
-> Each on a separate line.
+>
+> Open your `Project.toml` and add `name = `, `uuid = `
+> and optionally an `authors` field, each on a separate line.
{: .challenge}
Now Melissa can use
+
~~~
pkg> dev . # or path to package instead of `.`
~~~
{: .language-julia}
-instead of needing to `includet` `MelissasModule.jl` and use `using MelissasModule` instead of `.using MelissasModule`.
+
+instead of needing to `includet MelissasModule.jl`, and she can write
+`using MelissasModule` instead of `.using MelissasModule`.
## Register a package
-In order for her friends to be able to get the package the registers the package in the _general registry_.
-Either via [juliahub](https://juliahub.com/ui/Registrator) or by making a pull request on [github](https://github.com/JuliaRegistries/General/pulls) which can also be automated by the [julia registrator](https://github.com/JuliaRegistries/Registrator.jl).
+In order for her friends to be able to get the package, Melissa registers the
+package in the _general registry_.
+This can be done either via [juliahub][jh] or by making a pull request on
+[GitHub][gh] which can also be automated by the [Julia registrator][jr].
## Creating a new package
Melissa thinks next time she will start with a package right away.
-Browsing the packages she found [PkgTemplates.jl](https://invenia.github.io/PkgTemplates.jl/stable/) and [PkgSkeleton.jl](https://github.com/tpapp/PkgSkeleton.jl) which makes setting up the typical folder structure very easy.
+Browsing the packages she found [PkgTemplates.jl][pt] and [PkgSkeleton.jl][ps]
+which makes setting up the typical folder structure very easy.
> ## Create your own package
-> Look at the documentation of the package creation helper packages and create a new package using `generate`.
+>
+> Look at the documentation of the package creation helper packages and create
+> a new package using `generate`.
{: .challenge}
+[ex]: https://github.com/JuliaLang/Example.jl
+[gh]: https://github.com/JuliaRegistries/General/pulls
+[jh]: https://juliahub.com/ui/Registrator
+[jr]: https://github.com/JuliaRegistries/Registrator.jl
+[ps]: https://github.com/tpapp/PkgSkeleton.jl
+[pt]: https://invenia.github.io/PkgTemplates.jl/stable/
+
{% include links.md %}
diff --git a/_episodes/09-tests.md b/_episodes/09-tests.md
index 794a87de..01277906 100644
--- a/_episodes/09-tests.md
+++ b/_episodes/09-tests.md
@@ -4,7 +4,7 @@ teaching: 10
exercises: 30
questions:
- "What are unit tests?"
-- "How are tests organized in julia?"
+- "How are tests organized in Julia?"
objectives:
- "Learn to create unit tests and test-sets using the `Test` standard library"
keypoints:
@@ -13,23 +13,27 @@ keypoints:
## Unit tests
-Now that Melissa has released her first package she fears that future changes will impact the existing functionality of her package.
+Now that Melissa has released her first package she fears that future changes
+will impact the existing functionality of her package.
This can be prevented by adding tests to her package.
-Looking at the structure of other packages Melissa figured out that tests usually go in a separate `test` folder next to the `src` folder.
+Looking at the structure of other packages Melissa figured out that tests
+usually go in a separate `test` folder next to the `src` folder.
This should contain a `runtests.jl` file.
-The standard library `Test` provides the functionality for writing tests.
-Namely, the macros `@test` and `@testset`.
+The standard library `Test` provides the functionality for writing tests:
+namely, the macros `@test` and `@testset`.
+
+`@test` can be used to test a single equality, such as
-`@test` can be used to to test a single equality, such as
~~~
using Test
@test 1 + 1 == 2
~~~
{: .language-julia}
-several tests can be grouped in a test-set with a descriptive name
+Several tests can be grouped in a test-set with a descriptive name
+
~~~
using Test
@testset "Test arithmetic equalities" begin
@@ -39,6 +43,7 @@ end
{: .language-julia}
With this Melissa can run her test using the pkg mode of the REPL:
+
~~~
(MelissasModule) pkg> test
~~~
@@ -46,18 +51,27 @@ With this Melissa can run her test using the pkg mode of the REPL:
### Test specific dependencies
-Melissa needed to add `Test` to her package in order to run the code above, but actually `Test` is not needed for her package other than testing.
-Thus it is possible to move the `Test` entry in the `Project.toml` file from `[deps]` to an `[extras]` section and then add another entry
+Melissa needed to add `Test` to her package in order to run the code above, but
+actually `Test` is not needed for her package other than testing.
+Thus it is possible to move the `Test` entry in the `Project.toml` file from
+`[deps]` to an `[extras]` section and then add another entry:
+
~~~
[targets]
test = ["Test"]
~~~
{: .language-julia}
-Check out the [sample project file](../code/Project.toml) for a complete example.
+
+Check out the [sample project file](../code/Project.toml) for a complete
+example.
> ## Create a test for MelissasModule
-> Create a test that ensures that `shoot_distance` returns a value that is between `target - ε` and `target + ε`.
+>
+> Create a test that ensures that `shoot_distance` returns a value that is
+> between `target - ε` and `target + ε`.
+>
> > ## Solution
+> >
> > ~~~
> > using MelissasModule
> > using Test
@@ -70,7 +84,8 @@ Check out the [sample project file](../code/Project.toml) for a complete example
> > # default ε is 1e-1
> > end
> > ~~~
-> >{: .language-julia}
->{: .solution}
+> > {: .language-julia}
+> {: .solution}
{: .challenge}
+{% include links.md %}
diff --git a/index.md b/index.md
index 7caa3952..bb6ea576 100644
--- a/index.md
+++ b/index.md
@@ -3,13 +3,17 @@ layout: lesson
root: . # Is the only page that doesn't follow the pattern /:path/index.html
permalink: index.html # Is the only page that doesn't follow the pattern /:path/index.html
---
-![Trebuchet](https://upload.wikimedia.org/wikipedia/commons/e/ee/Trebuchet_Scheme.svg){: height="400px"}
+![Trebuchet][trebuchet]{: height="400px"}
-Melissa and her schoolmates built a trebuchet in their physics-club.
-In their first tests they experienced that aiming it is not that easy and intuitive, so they are planning to write a program that automatically aims their trebuchet given the distance of the target and the velocity of the wind.
-As programming language they chose julia since it has state-of-the-art packages for this tasks and they also read it composes very well.
+Melissa and her schoolmates built a trebuchet in their physics club.
+In their first tests they experienced that aiming it is not that easy and
+intuitive, so they are planning to write a program that automatically aims
+their trebuchet given the distance of the target and the velocity of the wind.
-Unfortunately nobody knows this language already, so they have to learn it from scratch.
+As for the programming language, they chose Julia since it has state-of-the-art
+packages for this task and they also read it composes very well.
+Unfortunately nobody knows this language already, so they have to learn it from
+scratch.
@@ -17,10 +21,14 @@ Unfortunately nobody knows this language already, so they have to learn it from
> ## Prerequisites
>
-> You need to understand the concepts of **files** and **directories** as well as **arrays** and **indices**.
-> It is benficial to have some programming experience in another language.
+> You need to understand the concepts of **files** and **directories** as well
+> as **arrays** and **indices**.
>
-> This lesson requires julia 1.6 or greater
+> It is beneficial to have some programming experience in another language.
+>
+> This lesson requires Julia 1.6 or greater
{: .prereq}
+[trebuchet]: https://upload.wikimedia.org/wikipedia/commons/e/ee/Trebuchet_Scheme.svg
+
{% include links.md %}
diff --git a/setup.md b/setup.md
index 288c8d75..b9164e74 100644
--- a/setup.md
+++ b/setup.md
@@ -3,8 +3,8 @@ title: Setup
---
- Download the latest stable release of Julia from [julialang.org](https://julialang.org/downloads/), make sure that your Julia version is 1.6 or higher.
-- On windows it is recommended to install julia via the windows store.
+- On windows it is recommended to install Julia via the windows store.
- If you haven't already, install a terminal editor, for example [nano](https://www.nano-editor.org/download.php).
-- For this lesson it is recommended to start julia by running julia from a bash-shell
+- For this lesson it is recommended to start Julia by running `julia` from a Bash shell
{% include links.md %}