Skip to content

Instructor's Note

CC BY-SA 4.0

Part 1: variables and types

All scripts corresponding to this part of the workshop can be found in the 01-variables folder. We suggest starting with 01-numeric.jl and moving on according to the numbering system (e.g after 01-numeric.jl, go through 02-booleans.jl), but you could adjust this according to your needs. In any case, we suggest leaving 04-containers.jl for the end, as going through the concepts on that script will probably be easier after covering the content on the other ones.

When showcasing 01-numeric.jl, start explaining that Julia has extensive support for numeric variables, but that most users will use and come across Floats and Integers. Go over the definition of Integers, what they mean and how variables are assigned in Julia. Make sure to mention the use of underscores (_) as a useful tool to define large numbers. Next, go over Floats following a similar approach to the one used to explain integers. Mention that Floats are also known as doubles in other programming languages. Show how you can create the same values as before, but as Floats (e.g, 1 and 1.0) and use this opportunity to explain the use of typeof to check a variable's type. Here you can show how to define numbers in scientific notation. Lastly, go over all the math operations, mentioning the PEMDAS order of operations, how to calculate square roots with \sqrt<TAB> and briefly showcasing rational and complex numbers.

For 02-booleans.jl, start by showing the possible comparisons between numbers (less than, greater than, etc.). After that, explain the use of ! for negation, || for OR and && for AND. Make sure to go over how these operators work if needed. There could be questions about using | and & instead of || and &&. If that is the case, explain the difference between boolean operators (two operators) and bitwise operators (single operator). Next, go over the true and false types, using the examples to showcase how they work and further explain the use of the operators. Mentioning that in Julia these types start with lower case could be useful if there are Python users in the audience. Lastly, show how boolean values can be used in math operations.

When going over 03-strings.jl, begin by showing how strings are created in Julia using double quotes. Mentioning that Julia doesn't support creating strings with single quotes is probably a good idea, specially if there are R or Python users in the audience. Also, make sure to mention the use of triple quotes as an alternative to create longer strings spanning more than one line. For the next part, go over some of the string manipulations, starting with joining strings and showing the different methods on the script. Next, go over the use of contains and occursin for pattern matching and the formatting functions (e.g, uppercase).

Lastly, cover 04-containers.jl. Start by showing how Vectors are created using the examples to show that they can contain any type of variables. Next, go over matrices and the two different methods that could be used to create a matrix (line breaks and ;). As an alternative, show the use of reshape to create a matrix from a Vector, and use this opportunity to discuss what Julia being column-major means. After that, show how indexing and slicing works for both Vectors and matrices. Lastly, go over dictionaries. Start by showing how to create them using Tuples and the Pairs, and how to retrieve the values by indexing using the keys.

Part 2: syntax

All scripts corresponding to this part of the workshop can be found in the 02-syntax folder. In this case, it is recommended to start with 01-conditionals.jl and follow the numbering scheme, as later scripts (e.g. 02-loops.jl) use concepts from the previous ones.

When covering the content from 01-conditionals.jl, start by providing a brief description of what conditional statements are and how they allow creating more complex programs by altering the flow of execution. Once everyone is comfortable with that idea, show how they are created in Julia through the if, elseif and else keywords. Make sure to mention that Julia requires the keyword end to be present at the end, as this could be confusing for users coming from other programming languages. After that, go over the example that uses two elseif and doesn't have an else case, and use it to explain that conditional statements can be created in many different ways and that only if is required to create one. Lastly, explain how the ternary operator (?:) works and how it can be useful to write if-else statements concisely.

Next, go over 02-loops.jl. Once again, start by explaining that they are used to alter a program's flow of execution through the repeated evaluation of an expression. Start by explaining for loops with the simple example that prints all numbers from 1 to 10. After going over this example, make sure to mention how using the for loop saved a lot of work for this task. Two good ways to convey this idea could be showing how that would be done without a loop (manually write println(1), println(2), etc.) or changing the range to be much larger (e.g. numbers = 1:1000) so that the hard-coded solution is no longer feasible. After that, go over the example that combines a for loop and a conditional statement to showcase this common pattern.

The next part of 02-loops.jl covers the use of while loops. Start by explaining that while loops are different to for loops in that their iterations depend on the value of a boolean variable, instead of a collection or an iterable. Use the same example of printing all numbers from 1 to 10, focusing on how the counter changes until it reaches 11 and provide a brief explanation on why global is used when updating it's value. Next, go over the example of finding a name inside of a list. Use this example to explain why while loops are convenient when it is not necessary to iterate over an entire collection and how this example could be extended for a case where there are many more names in the list. After that, show how a similar result can be obtained with a for loop, a conditional statement and the break keyword. This example is also a good opportunity to discuss one of the main disadvantages of while loops, which is that you can easily create infinite loops. Going back to the previous examples might be required to show how an infinite loop can be created (e.g. the name you are looking for is not in the list or you forget to update the counter). Lastly, go over array comprehensions and how they can be used to create arrays with loops.

After that, cover the content on 03-assignments.jl. This script goes over different topics regarding variable assignments, starting with the use of compound expressions. Show the different ways in which compound expressions can be created, but mention that using begin..end with multiple lines is probably the most common and readable syntax. The next topic is variable scopes. Show how global variables can be accessed everywhere and explain with greater detail why global is used inside while loops. Next, showcase the use of local and how local variables are only accessible where they are created (inside the loop, in the example). After that, go over the use of const to create variables whose values should not change. Use the examples to show that trying to change the value returns an error if the new value is of a different Type, but that you only get a warning if the new value has the same Type as the original. Finally, explain the variable naming conventions in Julia with an emphasis on how variables and functions are named in a different way to Types and Modules.

For 04-modules.jl, start by going over the concept of a software Module/package/library as a tool to access other people's code. Go over how packages are imported with the using keyword by showing how to import Pumas and Statistics. Show that we have access to the mean function after importing Statistics. Mention that multiple packages can be imported in a single line, but explain that this syntax quickly becomes difficult to read and that importing packages in different lines is preferred. Also, show how only certain parts of the package can be loaded with the Package: func syntax. After that, show the use of import and explain how it is different from using.

Lastly, go over 05-macros.jl. Start by mentioning that the purpose of this lesson is to show how macros are used, and that creating macros and using Julia's metaprogramming tools is an advanced topic that will not be covered in the workshop. Curious attendees can be redirected to Julia's documentation section on metaprogramming, this workshop on metaprogramming from JuliaCon 2021 or other resources you might consider relevant.

First, show that macros start with @ and quickly go over the examples. Next, show the two ways in which macros can be called: using spaces or parenthesis (as in a function). The @time and @doc macros are used in the examples, so it is a good idea to briefly talk about what those macros do. Lastly, show how @macroexpand can be used to see what a macro is doing under the hood.

Part 3: functions

The code examples for this section of the workshop can be found in the 03-functions folder.

Start by going over 01-syntax.jl as this script will cover the basics of defining and calling functions. First, show the standard way of creating functions with the function keyword, and then show how a function can be called once it has been defined. Use the examples to show that a function can have a single argument, multiple arguments, or no arguments at all. Next, go over the compact assignment form to create functions, how that can be convenient for small or simple functions and how it allows creating functions in a very similar way to math. Lastly, go over the return keyword, how it can be used to return multiple values and how those values can be retrieved after a function call.

Next, cover the contents of 02-advanced.jl. This file contains code examples about a variety of advanced topics regarding the use of functions in Julia. The first topic is documentation. Show how you can add docstrings on top of a function's definition and how you can then access that documentation in the REPL by typing ?function_name. If there are users that know how to write Markdown, mention that docstrings are interpreted as Markdown when displayed in the REPL. After that, go over argument and return Types. Start by explaining how you can specify the accepted argument Types by showing the examples that work and the ones that produce errors. Once that is clear, go over specifying the return Type, which should be a natural extension of what was shown before.

The next advanced topic is default values for arguments. Go over the example to show how the syntax and make sure to explain how defining default values could be useful. It is probably important to mention that positional arguments with default values should always be left for the end (e.g f(x, y=2) instead of f(y=2, x)). Next, cover the use of keyword arguments. Start by explaining that keyword arguments are different from positional arguments in that they are identified through their names, not their positions. Then, go over the examples to show how they are used,and make sure to mention that it is a good practice to separate keyword arguments from positional arguments with a semicolon (;). It might be useful to mention that many packages use keyword arguments, specially for functions that have many arguments, which are particularly common in plotting packages such as CairoMakie or AlgebraOfGraphics.

Lastly, go over anonymous functions. Use the example to show how they can be convenient in some cases. Examples using functions like map, foreach or filter could be useful, but the use of these functions will only be covered in detail until the next part of the workshop.

The last script for this part of the workshop is 03-dispatch.jl. This is intended to be a brief introduction to multiple dispatch in Julia. Start by showing again how argument Types can be specified in a function. This should be brief as this topic was covered before. Next, use the code example to explain that sometimes it could be useful to have one function that behaves differently depending on the arguments that are passed to it. Show how you can "define the function again" with different argument Types and how this creates another method for that function. To finish, go over the methods function, which allows you to see a list of the available methods for a function. Show how to use it with both the function from the example and a function from Julia's standard library.

Part 4: functional programming

The files for this part of the workshop are located in the 04-functional_programming folder.

Start by going over the content on 01-apply.jl. This script contains examples on the use of vectorized functions, map and foreach to apply a function to all elements in an array. To begin, use the example to show how the vectorized version of a function can be used to apply a function to a collection in a concise syntax. Make sure to mention that this behavior is available for in any Julia function. Using an example from a function that was not defined in the script might be useful for this. Also, cover the use of the @. macro as a convenient tool to write expressions that require using the vectorized version of many functions.

Next, go over the use of the map function, which allows getting the same results as before by specifying the function and the collection as it's arguments. Explain the examples and make special emphasis on the use of map with anonymous functions, since this is a common use case for the function. After that, explain how the foreach function works. Here, is it very important to make sure that users understand the difference between map and foreach.

After that, move on to the 02-reduce.jl script. This part of the lesson is focused on the use of reduce and mapreduce to collapse containers into a single value. Start by showing the simple examples involving + and * and then go over the more complex one involving a custom function. Make sure to provide a detailed explanation on how this function is created as it could be confusing at first, but note that reduce is often used with + and *. Users might be curious as to why it was necessary to provide an initial value in the example for the custom function and not before. If that is the case, you can answer that the init keyword argument is required for all functions except +, *, min and max. Next, cover the common use pattern of reduce + map and mention that the same results can be achieved with mapreduce, but with improved performance. You can find more information about this in mapreduce's docstring.

The next script that you should go over is 03-filter.jl. The code examples in this file focus on the use of the filter function, which allows retrieving the elements of an array that satisfy a given condition. Go over the syntax, highlighting the fact that it is the same as map, foreach and reduce. You might need to spend extra time in the example about removing missing values. In particular, make sure to explain why it is necessary to use the ismissing function. Lastly, show the use of the count function as an alternative to filter when you are only interested in knowing how many elements satisfy a condition (as opposed to knowing which elements satisfy the condition).

Finally, go over the contents on 04-composition.jl. This is a brief introduction to composing and chaining function calls. Start by going over the composition operator (). Show that this is an alternative to calling multiple functions inside each other (e.g func1(func2(func3(args)))) and explain how it allows combining simple functions to create a more complicated one. Also, make sure to showcase how you can define a new function from the composition of multiple functions through the assignment syntax (new_function = func1 ∘ func2 ∘ func3) and that you can define the function and call it in one go ((func1 ∘ func2 ∘ func3)(args)). It is likely that users won't know how to type , so make sure to mention that it is created with \circ<TAB>.

The last topic for this lesson is function chaining through the pipe operator (|>). Show how it can be used to get the same results as with function composition, but with a different syntax. Make sure to mention that parenthesis are required when using anonymous functions and that the pipe operator can be used in a vectorized version (.|>).

Get in touch

If you have any suggestions or want to get in touch with our education team, please send an email to training@pumas.ai.

License

This content is licensed under Creative Commons Attribution-ShareAlike 4.0 International.

CC BY-SA 4.0