From 3fa46ef25d315080feebd0fcc857351b3164b195 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Thu, 2 Dec 2021 14:41:18 -0500 Subject: [PATCH 1/4] julia -> Julia, where appropriate --- README.md | 4 ++-- _episodes/01-introduction.md | 4 ++-- _episodes/02-REPL.md | 4 ++-- _episodes/03-types.md | 8 ++++---- _episodes/04-pkg.md | 4 ++-- _episodes/05-functions.md | 8 ++++---- _episodes/06-control-flow.md | 8 ++++---- _episodes/07-modules.md | 4 ++-- _episodes/08-packages.md | 6 +++--- _episodes/09-tests.md | 2 +- index.md | 4 ++-- setup.md | 4 ++-- 12 files changed, 30 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 3bb07c07..96421494 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# 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/) [![Our channel](https://img.shields.io/badge/juliaswc-slack_channel-orange)](https://swcarpentry.slack.com/archives/CBJ8C7NE6) -This lesson targets people who already have some experience with (scientific) programming, but are new to julia. +This lesson targets people who already have some experience with (scientific) programming, but are new to Julia. **Thanks for contributing to The Carpentries Incubator!** diff --git a/_episodes/01-introduction.md b/_episodes/01-introduction.md index f4c95756..c3fb2515 100644 --- a/_episodes/01-introduction.md +++ b/_episodes/01-introduction.md @@ -27,14 +27,14 @@ Interpreted languages such as python and R translate instructions one at a time, 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. -## 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_. 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. -There is another aspect of julia that makes it interesting and that is the way packages compose. +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. diff --git a/_episodes/02-REPL.md b/_episodes/02-REPL.md index c4bec13d..29733f0d 100644 --- a/_episodes/02-REPL.md +++ b/_episodes/02-REPL.md @@ -21,7 +21,7 @@ keypoints: ## Variables -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. +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 @@ -65,7 +65,7 @@ julia> varinfo() ## Unicode -In julia unicode characters are also allowed as variables like `α = 2`. +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 diff --git a/_episodes/03-types.md b/_episodes/03-types.md index db0179f3..d4781b4d 100644 --- a/_episodes/03-types.md +++ b/_episodes/03-types.md @@ -4,13 +4,13 @@ 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 @@ -43,7 +43,7 @@ end 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) @@ -122,7 +122,7 @@ Stacktrace: {: .error} > ## Caveat: redefining `struct`s -> In julia it is not very easy to redefine `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. {: .callout} diff --git a/_episodes/04-pkg.md b/_episodes/04-pkg.md index 345a023d..4cc66085 100644 --- a/_episodes/04-pkg.md +++ b/_episodes/04-pkg.md @@ -19,11 +19,11 @@ 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`. +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. +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 diff --git a/_episodes/05-functions.md b/_episodes/05-functions.md index 0f4ba1b0..b2438ef6 100644 --- a/_episodes/05-functions.md +++ b/_episodes/05-functions.md @@ -13,7 +13,7 @@ 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 @@ -35,7 +35,7 @@ help?> names {: .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: +> 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. @@ -121,7 +121,7 @@ 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. +> 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} @@ -197,7 +197,7 @@ Base > ## 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. +> Macros can transform any valid Julia expression and are quite powerful. > They can be expanded using `@macroexpand`. {: .callout} diff --git a/_episodes/06-control-flow.md b/_episodes/06-control-flow.md index f17d3ebb..48dca69f 100644 --- a/_episodes/06-control-flow.md +++ b/_episodes/06-control-flow.md @@ -20,7 +20,7 @@ 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`. +In Julia the keywords to specify conditions are `if`, `elseif` and `else`. Closed with an `end`. Thus she writes @@ -39,9 +39,9 @@ end ### Interfaces -`setindex!` is actually one function of a widespread _interface_ in the julia language: `AbstractArray`s. +`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. +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 %}). @@ -91,7 +91,7 @@ But first Melissa needs a way to improve her parameters. > 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](https://en.wikipedia.org/wiki/Automatic_differentiation) and the julia package `ForwardDiff.jl` gradients can be calculated easily. +> Thanks to [automatic differentiation](https://en.wikipedia.org/wiki/Automatic_differentiation) and the Julia package `ForwardDiff.jl` gradients can be calculated easily. {: .quotation} 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. diff --git a/_episodes/07-modules.md b/_episodes/07-modules.md index 3ec8f547..decd46e9 100644 --- a/_episodes/07-modules.md +++ b/_episodes/07-modules.md @@ -14,7 +14,7 @@ keypoints: ## 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 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. 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. @@ -155,7 +155,7 @@ If she changes the code in the module, reincludes the module and runs her code a ## 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 - `using Revise` must come before `using` any Package that she wants to be tracked diff --git a/_episodes/08-packages.md b/_episodes/08-packages.md index 655ed754..93175b6e 100644 --- a/_episodes/08-packages.md +++ b/_episodes/08-packages.md @@ -15,14 +15,14 @@ keypoints: 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`. A `uuid` is a universally unique identifier. -Thankfully julia comes with the `UUIDs` package, that can generate `uuid`s for Melissa via `UUIDs.uuid4()`. +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 @@ -57,7 +57,7 @@ instead of needing to `includet` `MelissasModule.jl` and use `using MelissasModu ## 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). +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). ## Creating a new package diff --git a/_episodes/09-tests.md b/_episodes/09-tests.md index 794a87de..3558b962 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: diff --git a/index.md b/index.md index 7caa3952..7dc826c9 100644 --- a/index.md +++ b/index.md @@ -7,7 +7,7 @@ permalink: index.html # Is the only page that doesn't follow the pattern /:path 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. +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. Unfortunately nobody knows this language already, so they have to learn it from scratch. @@ -20,7 +20,7 @@ Unfortunately nobody knows this language already, so they have to learn it from > 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. > -> This lesson requires julia 1.6 or greater +> This lesson requires Julia 1.6 or greater {: .prereq} {% 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 %} From 34a22d7e9758d90a0070ee9beac1c42d95e40b45 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Fri, 3 Dec 2021 05:52:13 -0500 Subject: [PATCH 2/4] Lesson grammar check; remove HTML tags from MD --- _episodes/01-introduction.md | 25 +++++----- _episodes/02-REPL.md | 30 ++++++------ _episodes/03-types.md | 28 +++++++----- _episodes/04-pkg.md | 22 +++++---- _episodes/05-functions.md | 72 +++++++++++++++++++++-------- _episodes/06-control-flow.md | 89 +++++++++++++++++++++--------------- _episodes/07-modules.md | 23 ++++++---- _episodes/08-packages.md | 23 ++++++---- _episodes/09-tests.md | 22 ++++++--- index.md | 6 +-- 10 files changed, 208 insertions(+), 132 deletions(-) diff --git a/_episodes/01-introduction.md b/_episodes/01-introduction.md index c3fb2515..65b99088 100644 --- a/_episodes/01-introduction.md +++ b/_episodes/01-introduction.md @@ -3,13 +3,14 @@ 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? @@ -20,19 +21,19 @@ This specification will be translated to machine instructions, the so called ass ## 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. +Most prominently, compiled languages need an explicit type declaration for each variable. ## 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_. +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): @@ -46,10 +47,10 @@ This is captured the best by an analogy from [Sam Urmy](https://github.com/ElOce > 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? +> 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](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 +> 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). diff --git a/_episodes/02-REPL.md b/_episodes/02-REPL.md index 29733f0d..ee368a76 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." --- @@ -21,8 +21,9 @@ keypoints: ## Variables -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 +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: + ~~~ 1 + 4 * 7.3 ~~~ @@ -33,7 +34,7 @@ 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 +48,8 @@ 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 + ~~~ julia> varinfo() name size summary @@ -65,12 +66,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,7 +84,7 @@ 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. @@ -88,7 +92,7 @@ It can be used to issue commands of the underlying shell, but don't confuse it w > 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 d4781b4d..c687c124 100644 --- a/_episodes/03-types.md +++ b/_episodes/03-types.md @@ -17,14 +17,16 @@ keypoints: 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 @@ -62,6 +64,7 @@ So we have the relationship `Float64 <: AbstractFloat <: Real <: Number <: Any`, `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 @@ -72,6 +75,7 @@ All the other types are _abstract_ types that are used to address groups of type 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 +86,13 @@ 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 @@ -97,7 +101,7 @@ Have a look at this visualization of all subtypes of `Number`: > > > ## 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} +> {: .solution} {: .challenge} @@ -105,6 +109,7 @@ Have a look at this visualization of all subtypes of `Number`: 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,6 +127,7 @@ 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. diff --git a/_episodes/04-pkg.md b/_episodes/04-pkg.md index 4cc66085..afad26a1 100644 --- a/_episodes/04-pkg.md +++ b/_episodes/04-pkg.md @@ -22,17 +22,19 @@ Now it is time for Melissa and their mates to simulate the launch of the trebuch 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 ]. +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 + ~~~ (v1.x) pkg> activate projects/trebuchet ~~~ {: .language-julia} In this environment she adds the `Trebuchet` package by typing + ~~~ (trebuchet) pkg> add Trebuchet ~~~ @@ -40,8 +42,8 @@ In this environment she adds the `Trebuchet` package by typing 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` @@ -57,9 +59,10 @@ 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. +"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. + ~~~ (trebuchet) pkg> status ~~~ @@ -68,18 +71,19 @@ 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 b2438ef6..96c7fa55 100644 --- a/_episodes/05-functions.md +++ b/_episodes/05-functions.md @@ -20,8 +20,9 @@ keypoints: 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 @@ -33,14 +34,18 @@ help?> names 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`. +> 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`. +> 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} @@ -55,8 +60,9 @@ help?> names > 4. `names(Trebuchets, all)` > 5. Answer 2. and 3. > -> >## Solution -> > its 5 +> > ## Solution +> > +> > Option 5 is correct. > {: .solution} {: .challenge} @@ -75,8 +81,9 @@ 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_. +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 @@ -86,33 +93,52 @@ help?> Trebuchets.shoot 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_. +> +> 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.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 +) ~~~ {: .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 +So she tries again and grabs the second element this time: + ~~~ julia> Trebuchets.shoot(5, 0.25pi, 500)[2] 117.8468674726198 ~~~ {: .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. 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,6 +147,7 @@ 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. {: .callout} @@ -128,6 +155,7 @@ julia> function shoot_distance(windspeed, angle, weight) ### Adding methods 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) @@ -139,8 +167,9 @@ This method will call the former method and pass the correct fields from the `Tr ### 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](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: + ~~~ julia> function shoot_distance(args...) # slurping Trebuchets.shoot(args...)[2] # splatting @@ -151,16 +180,16 @@ 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 +202,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 +215,11 @@ 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` which can be used to obtain the module where the function is defined. +There is a handy _macro_ named `@which` that obtains the module where the function is defined. + ~~~ julia> @which setindex! Base @@ -196,6 +227,7 @@ 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. > They can be expanded using `@macroexpand`. diff --git a/_episodes/06-control-flow.md b/_episodes/06-control-flow.md index 48dca69f..d24485eb 100644 --- a/_episodes/06-control-flow.md +++ b/_episodes/06-control-flow.md @@ -19,11 +19,10 @@ 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 @@ -41,40 +40,42 @@ end `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. +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. @@ -82,14 +83,15 @@ A better way to do this is to use loops. But first Melissa needs a way to improve her parameters. -> ## Digression: gradients +> ## 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. > > 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. > -> 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. +> 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](https://en.wikipedia.org/wiki/Automatic_differentiation) and the Julia package `ForwardDiff.jl` gradients can be calculated easily. {: .quotation} @@ -97,6 +99,7 @@ But first Melissa needs a way to improve her parameters. 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`? > > 1. `] install ForwardDiff` @@ -104,20 +107,22 @@ Melissa uses the `gradient` function of `ForwardDiff.jl` to get the direction in > 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 @@ -134,6 +139,7 @@ julia> grad = gradient(x -> (shoot_distance([environment.wind, x[2], x[1]] - env 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,6 +151,7 @@ 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? {: .discussion} @@ -152,6 +159,7 @@ That got shorter, but also a bit too short. 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,9 +179,12 @@ 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 > > ![distance-surface](../fig/shoot_surface.png) @@ -181,7 +192,7 @@ julia> shoot_distance(better_trebuchet, environment) {: .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 @@ -212,3 +223,5 @@ julia> shoot_distance(better_trebuchet, environment) {: .language-julia} That is more what she had in mind. + +{% include links.md %} diff --git a/_episodes/07-modules.md b/_episodes/07-modules.md index decd46e9..783f18a3 100644 --- a/_episodes/07-modules.md +++ b/_episodes/07-modules.md @@ -9,17 +9,17 @@ 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. - This is what it looks like: + ~~~ import Trebuchet as Trebuchets using ForwardDiff: gradient @@ -70,7 +70,6 @@ imprecise_trebuchet = Trebuchet(500.0, 0.25pi) environment = Environment(5, 100) precise_trebuchet = aim(imprecise_trebuchet, environment) shoot_distance(precise_trebuchet, environment) - ~~~ {: .language-julia} @@ -80,11 +79,12 @@ She also recognizes that she has a bunch of definitions at the beginning that sh 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. +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 +134,11 @@ function aim(trebuchet::Trebuchet, environment::Environment; ε = 1e-1, η = 0.0 return Trebuchet(better_trebuchet[1], better_trebuchet[2]) end end # MelissasModule - ~~~ {: .language-julia} The rest of the code goes to a file she calls `MelissasCode.jl`. + ~~~ using .MelissasModule @@ -151,17 +151,19 @@ shoot_distance(precise_trebuchet, environment) 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. -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") +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") Thus she now runs + ~~~ julia> using Revise @@ -175,6 +177,7 @@ julia> include("MelissasCode.jl") 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 93175b6e..f343eb3b 100644 --- a/_episodes/08-packages.md +++ b/_episodes/08-packages.md @@ -7,9 +7,9 @@ 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" --- @@ -21,11 +21,12 @@ This way she can also locally use Julia's package manager for managing her modul 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. +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`](https://github.com/JuliaLang/Example.jl) which has the following structure: + ~~~ ├── docs │   ├── make.jl @@ -43,21 +44,24 @@ 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 use `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](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). ## Creating a new package @@ -66,6 +70,7 @@ 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. > ## Create your own package +> > Look at the documentation of the package creation helper packages and create a new package using `generate`. {: .challenge} diff --git a/_episodes/09-tests.md b/_episodes/09-tests.md index 3558b962..207d4993 100644 --- a/_episodes/09-tests.md +++ b/_episodes/09-tests.md @@ -19,17 +19,19 @@ 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. 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 +41,7 @@ end {: .language-julia} With this Melissa can run her test using the pkg mode of the REPL: + ~~~ (MelissasModule) pkg> test ~~~ @@ -47,7 +50,8 @@ 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 +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"] @@ -56,8 +60,11 @@ test = ["Test"] 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 + ε`. +> > > ## Solution +> > > > ~~~ > > using MelissasModule > > using Test @@ -70,7 +77,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 7dc826c9..7bd35bc5 100644 --- a/index.md +++ b/index.md @@ -5,10 +5,10 @@ permalink: index.html # Is the only page that doesn't follow the pattern /:path --- ![Trebuchet](https://upload.wikimedia.org/wikipedia/commons/e/ee/Trebuchet_Scheme.svg){: height="400px"} -Melissa and her schoolmates built a trebuchet in their physics-club. +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. +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. @@ -18,7 +18,7 @@ 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. +> It is beneficial to have some programming experience in another language. > > This lesson requires Julia 1.6 or greater {: .prereq} From e1f8672ce7d875a59739332762bafef2bc993aed Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Fri, 3 Dec 2021 07:18:29 -0500 Subject: [PATCH 3/4] wrap markdown at 79 lines; fix VSCode template Very long lines render poorly on GitHub and in editors, and (perhaps more significantly) require larger blobs for git to track changes. This commit wraps lines at 79 chars, for the most part, to improve rendering, readability, and repository size. `.editorconfig` has been updated to reflect the change. In addition, the VSCode template for Challenge-and-Solution blocks was modified to insert blank lines after each title. --- .editorconfig | 4 +- .vscode/carpentries.snippet.code-snippets | 29 +++--- _episodes/01-introduction.md | 72 ++++++++++---- _episodes/02-REPL.md | 28 ++++-- _episodes/03-types.md | 48 ++++++---- _episodes/04-pkg.md | 40 +++++--- _episodes/05-functions.md | 110 ++++++++++++++-------- _episodes/06-control-flow.md | 82 +++++++++++----- _episodes/07-modules.md | 59 ++++++++---- _episodes/08-packages.md | 41 +++++--- _episodes/09-tests.md | 19 ++-- index.md | 18 +++- 12 files changed, 371 insertions(+), 179 deletions(-) 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/_episodes/01-introduction.md b/_episodes/01-introduction.md index 65b99088..2662d1aa 100644 --- a/_episodes/01-introduction.md +++ b/_episodes/01-introduction.md @@ -15,44 +15,78 @@ keypoints: ## 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 on 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, compiled languages need an explicit type declaration for each variable. +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? -Julia is a programming language that superficially looks like an interpreted language and mostly behaves like one. +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 a 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 ee368a76..507d1fde 100644 --- a/_episodes/02-REPL.md +++ b/_episodes/02-REPL.md @@ -20,8 +20,11 @@ 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. +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: ~~~ @@ -34,7 +37,8 @@ The first thing they try is to perform basic arithmetic operations: {: .output} That works as expected. -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. +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_. ~~~ @@ -48,7 +52,8 @@ 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() @@ -67,13 +72,13 @@ 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`). +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?> ∂ @@ -86,13 +91,16 @@ She gets back to normal mode by pressing backspace. 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. +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 c687c124..1f4dbcea 100644 --- a/_episodes/03-types.md +++ b/_episodes/03-types.md @@ -15,7 +15,9 @@ keypoints: ## 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 structure types: @@ -24,8 +26,10 @@ There are two structure types: - 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. +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 @@ -42,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) @@ -59,9 +66,11 @@ 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: @@ -71,8 +80,10 @@ 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`: @@ -86,7 +97,8 @@ julia> subtypes(Real) ~~~ {: .language-julia} -This way the types form a tree with abstract types on the nodes and concrete types as leaves. +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) @@ -100,15 +112,19 @@ 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`. +> > +> > 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} @@ -129,8 +145,8 @@ Stacktrace: > ## 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. +> 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 afad26a1..2d9cdb24 100644 --- a/_episodes/04-pkg.md +++ b/_episodes/04-pkg.md @@ -18,15 +18,22 @@ 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. +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 @@ -40,7 +47,8 @@ In this environment she adds the `Trebuchet` package by typing ~~~ {: .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 @@ -58,10 +66,15 @@ 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 @@ -73,14 +86,17 @@ After the installation finished she can check the packages present in her enviro Now that Melissa added the package to her environment, she needs to load it. 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 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`. +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 Melissa defined 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`: diff --git a/_episodes/05-functions.md b/_episodes/05-functions.md index 96c7fa55..d945d509 100644 --- a/_episodes/05-functions.md +++ b/_episodes/05-functions.md @@ -18,9 +18,11 @@ keypoints: ## 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. -Julia's `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: ~~~ @@ -28,10 +30,13 @@ 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} @@ -40,19 +45,22 @@ help?> names > 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. +> 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)` @@ -66,8 +74,8 @@ help?> names > {: .solution} {: .challenge} - Thus Melissa executes + ~~~ julia> names(Trebuchets) 6-element Vector{Symbol}: @@ -81,8 +89,10 @@ 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 @@ -90,45 +100,49 @@ 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. +> 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. ~~~ 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), +(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.8468674726198, -1.5239999999999974), - Trebuchet.Vec(10.790333612654146, -21.45379494231241), + Trebuchet.Vec(117.8, -1.524), + Trebuchet.Vec(10.79, -21.45), Solution(394), 0, Val{:Released}() ), - 117.8468674726198 + 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. +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} @@ -136,7 +150,8 @@ 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: ~~~ @@ -148,13 +163,16 @@ julia> function shoot_distance(windspeed, angle, weight) > ## 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) @@ -163,12 +181,15 @@ 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 @@ -180,7 +201,8 @@ julia> function shoot_distance(args...) # slurping ### Anonymous functions -Sometimes it is useful to have a new function and not have 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 with either the so-called stabby lambda notation, @@ -217,8 +239,10 @@ which tells her two things: 1. a function named `setindex!` was called 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` that obtains the module where the +function is defined. ~~~ julia> @which setindex! @@ -228,11 +252,15 @@ Base > ## Macros > -> Macro names begin with `@` and they don't need parentheses or commas to delimit their arguments. +> 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 d24485eb..1c3170fc 100644 --- a/_episodes/06-control-flow.md +++ b/_episodes/06-control-flow.md @@ -19,8 +19,11 @@ 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 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`. +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 ~~~ @@ -38,12 +41,20 @@ 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, 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 %}). +`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` @@ -75,32 +86,42 @@ Therefore she restarts her REPL. ## 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 take 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. +> 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. > -> 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 [_gradient_][grad] of a function gives the direction in which the return +> value will change by the largest amount. > -> Since the `shoot_distance` function has three input parameters, the gradient of `shoot_distance` will return a 3-element `Array`: +> 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](https://en.wikipedia.org/wiki/Automatic_differentiation) and the Julia package `ForwardDiff.jl` gradients can be calculated easily. -{: .quotation} +> 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` @@ -136,7 +157,8 @@ 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. @@ -152,13 +174,16 @@ 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) @@ -186,7 +211,9 @@ julia> shoot_distance(better_trebuchet, environment) > > ## 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} @@ -197,9 +224,11 @@ julia> shoot_distance(better_trebuchet, environment) ### 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) @@ -224,4 +253,7 @@ julia> shoot_distance(better_trebuchet, environment) 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 783f18a3..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: @@ -14,10 +14,14 @@ keypoints: ## Modules -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. +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: ~~~ @@ -71,18 +75,27 @@ 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. ~~~ @@ -135,7 +148,7 @@ function aim(trebuchet::Trebuchet, environment::Environment; ε = 1e-1, η = 0.0 end end # MelissasModule ~~~ -{: .language-julia} +{: .language-julia title="MelissasModule.jl"} The rest of the code goes to a file she calls `MelissasCode.jl`. @@ -147,20 +160,25 @@ 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, re-includes 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: -- `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 @@ -174,7 +192,8 @@ 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? > diff --git a/_episodes/08-packages.md b/_episodes/08-packages.md index f343eb3b..b52235e9 100644 --- a/_episodes/08-packages.md +++ b/_episodes/08-packages.md @@ -13,19 +13,24 @@ keypoints: - "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 Julia's 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 **u**niversally **u**nique **id**entifier. -Thankfully Julia comes with the `UUIDs` package, that can generate `uuid`s for Melissa via `UUIDs.uuid4()`. +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 @@ -45,8 +50,8 @@ She looks at the example package [`Example.jl`](https://github.com/JuliaLang/Exa > ## 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 @@ -56,22 +61,34 @@ 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, Melissa registers the package in the _general registry_. -This can be done 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 207d4993..01277906 100644 --- a/_episodes/09-tests.md +++ b/_episodes/09-tests.md @@ -13,10 +13,12 @@ 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: @@ -49,19 +51,24 @@ 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 > > diff --git a/index.md b/index.md index 7bd35bc5..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. +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 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. +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**. +> You need to understand the concepts of **files** and **directories** as well +> as **arrays** and **indices**. +> > 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 %} From 95b0b3c89377511129ddb8aac75e37658d449d5f Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Fri, 3 Dec 2021 07:28:01 -0500 Subject: [PATCH 4/4] tidy up the README --- README.md | 52 ++++++++++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 96421494..307926d6 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,35 @@ # 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