Julia Basics: REPL & Revise


Creation date: 2020-07-01

Tags: julia, basics, newcomers

This is part 2 of an ongoing series about Julia. If you missed part 1:

There is now also part 3 on how to use the package manager.

In this post I want show you how to use the REPL (Read-eval-print loop) and to use Revise.jl which in my opinion is THE extension everyone should use!

If you enjoy the video format more than reading a blog: First of all: Thanks that you're here and not on YouTube/Netflix/Twitch etc :D You might wanna have a look at this wonderful video about the REPL: Julia REPL tips and tricks by BrainRPG.

That are well spent 40 minutes ;) Anyway as you're here let's continue.

REPL first usage

After downloading/installing the latest version (now 1.4.2) you can just type julia in your REPL and get greeted like this:

> julia
               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.4.2 (2020-05-23)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

julia>

You get some useful information right there which newcomers often miss as they are too excited to try it out. Maybe you've been there and are now reading this post to get some more tips & tricks.

I'm talking about the different REPL modes:

You can simply type one of those characters to switch to the mode.

Getting help

After typing ? you see:

help?>

You can get help about functions like getting their docstring using this method.

In the last post I talked about methods to see which methods are implemented for a given function.

help?> methods
search: methods methodswith Method MethodError hasmethod

  methods(f, [types], [module])

  Return the method table for f.

  If types is specified, return an array of methods whose types match. If module is specified, return an array of methods defined in that module. A list of modules can also be specified as an array.

  │ Julia 1.4
  │
  │  At least Julia 1.4 is required for specifying a module.

The first line shows you a list of functions with a similar name which might be handy if you mistyped the function name.

Or if you just have a typo:

help?> metods
search: methods methodswith Method MethodError hasmethod

This way you also sometimes learn new useful functions.

You have no clue what the method is called? I guess you still now what you want to do with it ;)

Maybe have a look at apropos

julia> apropos("standard deviation")
Base.randn
Random.randn!
Statistics.stdm
Statistics.std

You're probably looking for Statistics.std. I have to admit that often this way you get quite a long list of possibilities but it can be a reasonable method if you don't have an internet connection.

In the end of this post I have a list of resources you might want to check out when learning Julia :)

Shell mode

The next mode in our list is the shell mode which you can enter with ;:

shell>

I don't actually know how well this works on Windows but it's quite useful on Linux and probably Mac.

You can just use your normal commands that you use in your shell like cd, ls, cp, pwd and so on. These are only the ones I use frequently. Depending on your workflow you might wanna use git with that as well.

It is often nice to be able to do a quick command in the same directory instead of opening a new terminal for it.

That said you can use Julia itself for some of those commands as well:

help?> cd
search: cd Cdouble gcd gcdx secd asecd cld Cmd codeunit codeunits codepoint code_llvm code_typed code_native code_lowered code_warntype @cmd ncodeunits @code_llvm @code_typed @code_native @code_lowered

  cd(dir::AbstractString=homedir())

  Set the current working directory.

Package mode

Let's get to the last REPL mode before discussing Revise a bit. You can find more about the package manager in the next part of this series where I talk more about environments and packages.

You can add packages with this mode as well as updating/removing them. Follow me with ].

(@v1.4) pkg>

This mode is a little special as you see the package environment we are in. At the moment we are in the global environment. If you need different package versions in different projects/environments, you can create a new environment by using activate.

(@v1.4) pkg> ?activate
  activate
  activate [--shared] path

  Activate the environment at the given path, or the home project environment if no path is specified. The active environment is the environment that is modified by executing package commands. When
  the option --shared is given, path will be assumed to be a directory name and searched for in the environments folders of the depots in the depot stack. In case no such environment exists in any of
  the depots, it will be placed in the first depot of the stack.

i.e if I want to add a dependency to my ConstraintSolver I would write

]
(@v1.4) pkg> activate /Users/olek/Code/ConstraintSolver
Activating environment at `~/Julia/ConstraintSolver/Project.toml`

(ConstraintSolver) pkg>

but that might be needed in your first steps so don't worry about that now :)

Instead let's install Revise.jl as I mentioned it's THE package you need. If you don't trust me, you might click on the link to check it out before you follow the installation.

(ConstraintSolver) pkg> activate 
Activating environment at `~/.julia/environments/v1.4/Project.toml`

<- Just to get back to the general environment for me. You probably don't have the ConstraintSolver so you don't need it.

(@v1.4) pkg> add Revise
   Updating registry at `~/.julia/registries/General`
   Updating git-repo `https://github.com/JuliaRegistries/General.git`
  Resolving package versions...
   Updating `~/.julia/environments/v1.4/Project.toml`
  [295af30f] + Revise v2.7.3
   Updating `~/.julia/environments/v1.4/Manifest.toml`
 [no changes]

And now v2.7.3 of Revise is installed. Simple, isn't it?

Before I jump into Revise let's stay in this REPL mode for a bit if you don't mind.

Checking which packages one has installed in the current environment:

(@v1.4) pkg> status
Status `~/.julia/environments/v1.4/Project.toml`
  [c7e460c6] ArgParse v1.1.0
  [c52e3926] Atom v0.12.15
  [67c07d97] Automa v0.8.0
  [6e4b80f9] BenchmarkTools v0.5.0
  [00ebfdb7] CSTParser v2.4.0
  ...
  [1986cc42] Unitful v1.1.0 [`~/.julia/dev/Unitful`]
  [44d3d7a6] Weave v0.9.4
  [ade2ca70] Dates 
  [8bb1440f] DelimitedFiles 
  [3fa0cd96] REPL

I don't wanna fill your screen ... I have quite a lot installed and can probably uninstall some of them. You can see the version number for each package here (besides standard libraries like Dates). For Unitful you can see that I have checked it out for development. (Just some test I think).

If you want to improve Revise (or any other package) you can use

(@v1.4) pkg> dev Revise

and then the repo will be cloned to ~/.julia/dev/Revise and you can implement your awesome feature.

Okay let's check whether there are some updates:

(@v1.4) pkg> up
Updating registry at `~/.julia/registries/General`
   Updating git-repo `https://github.com/JuliaRegistries/General.git`
   Updating git-repo `https://github.com/wookay/TwitchTools.jl`
  Installed Adapt ───────────────────── v2.0.2
  Installed Images ──────────────────── v0.22.4
  Installed HTTP ────────────────────── v0.8.16
  Installed Plots ───────────────────── v1.5.1
  Installed Pluto ───────────────────── v0.9.11
  Installed PackageCompiler ─────────── v1.2.0
  Installed Colors ──────────────────── v0.12.3
  ...
     Updating `~/.julia/environments/v1.4/Project.toml`
  [5ae59095] ↑ Colors v0.12.2 ⇒ v0.12.3
  [713c75ef] ↑ Franklin v0.8.5 ⇒ v0.8.6
  [cd3eb016] ↑ HTTP v0.8.15 ⇒ v0.8.16
  [916415d5] ↑ Images v0.22.3 ⇒ v0.22.4
  [9b87118b] ↑ PackageCompiler v1.1.1 ⇒ v1.2.0
  [91a5bcdd] ↑ Plots v1.4.3 ⇒ v1.5.1
  [c3e4b0f8] ↑ Pluto v0.9.10 ⇒ v0.9.11
   Updating `~/.julia/environments/v1.4/Manifest.toml`
  [79e6a3ab] + Adapt v2.0.2
  [3da002f7] ↑ ColorTypes v0.10.4 ⇒ v0.10.5
  [5ae59095] ↑ Colors v0.12.2 ⇒ v0.12.3
  ...

okay maybe I haven't done this for a bit but some packages get frequently updated ;)

Maybe it's a good moment to talk a bit about Project.toml and Manifest.toml without getting too deep. Each environment has both of these files.

Project.toml Manifest.toml

Let me give you an example:

For my ConstraintSolver (which btw you really should check out!)

Project.toml
name = "ConstraintSolver"
uuid = "e0e52ebd-5523-408d-9ca3-7641f1cd1405"
authors = ["Ole Kröger <o.kroeger@opensourc.es>"]
version = "0.2.2"

[deps]
Formatting = "59287772-0a20-5a39-b81b-1366585eb4c0"
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
MatrixNetworks = "4f449596-a032-5618-b826-5a251cb6dc11"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"

[compat]
Formatting = "^0.4.1"
JSON = "~0.18, ~0.19, ~0.20, ~0.21"
JuMP = "^0.21.3"
MathOptInterface = "^0.9.11"
MatrixNetworks = "^1"
julia = "1"

[extras]
Cbc = "9961bab8-2fa3-5c5a-9d89-47fab24efd76"
Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa"
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
GLPK = "60bf3e95-4087-53dc-ae20-288a0d20c6a6"
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["ForwardDiff", "LinearAlgebra", "Test", "JSON", "Cbc", "GLPK", "Combinatorics", "Random"]

The first block gives the name and version of the package. Then [deps] is a list of dependencies with their uuid. In this example my ConstraintSolver needs these 6 packages to function.

[compat] describes which versions I need for each of these packages. The symbols like ~ and ^ are best described in the docs.

[extras] and [targets] describe some packages I need for my test cases but are not required to use my solver.

Okay let's have a look at Manifest.toml:

...

[[Markdown]]
deps = ["Base64"]
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"

[[MathOptInterface]]
deps = ["BenchmarkTools", "CodecBzip2", "CodecZlib", "JSON", "JSONSchema", "LinearAlgebra", "MutableArithmetics", "OrderedCollections", "SparseArrays", "Test", "Unicode"]
git-tree-sha1 = "cd2049c055c7d192a235670d50faa375361624ba"
uuid = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
version = "0.9.14"

...

There all packages are defined which are dependencies of dependencies. It also defines exactly which version I used for testing it locally. This can be useful if you have a problem with a package and file a bug report. If you used the latest version of MathOptInterface and I thought it works (with my compat bounds) I can see that you used a different version than I do and can fix the compat bounds/file an issue at MathOptInterface or fix my solver such that it works.

Okay now you really want to know how to remove a package, right?

(@v1.4) pkg> rm Unitful
   Updating `~/.julia/environments/v1.4/Project.toml`
  [1986cc42] - Unitful v1.1.0 [`~/.julia/dev/Unitful`]
   Updating `~/.julia/environments/v1.4/Manifest.toml`
 [no changes]

Is there something else I wanted to mention?

A yeah you might want to install a special version of one package or a specific branch. The help mode ... helps ;)

(@v1.4) pkg> ?add
  add [--preserve=<opt>] pkg[=uuid] [@version] [#rev] ...

 
  Examples

  pkg> add Example
  pkg> add --preserve=all Example
  pkg> add Example@0.5
  pkg> add Example#master
  pkg> add Example#c37b675

Tips & Tricks

@which, @edit

You might wanna have a look at the previous post where I also talk a bit about some functions that help you to find out which function is called or how to edit it. I put it there as it's also relevant for multiple dispatch.

In short you can use @which to find out which method is called:

julia> @which 2+2
+(x::T, y::T) where T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8} in Base at int.jl:86

and use @edit to jump to it and have a look at that function and potentially edit it.

replay a Session

When you have:

julia> a = 2
2

julia> b = 3
3

julia> c = 4
4

and want to reply your session you can type a = [UP ARROW] such that a = 2 pops up. Then click enter to define it again. Afterwards you can use [DOWN ARROW] to get b = 3.

Copying to the REPL

It's possible to simply copy the code from above into the repl and the results as well as julia> is removed automatically such that you can just work with it 😉

Working with unicode

You can write unicode symbols like α with \alpha <TAB>. And you can see how to type it if you only have the symbol:

help?> α
"α" can be typed by \alpha<tab>

If you have some other tricks: Please comment at the end of this post 😉

Revise

Okay now let's really check out this package.

After you played around in your REPL for a while you might want to start your first project or just open a file and write some functions.

How about learning some code with Project Euler

The problem we try to solve:

If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. Find the sum of all the multiples of 3 or 5 below 1000.

I don't like to write functions in the REPL, so I open a file:

euler_1.jl:

function sum_mul35_below(below=1000)

end

Did I mention that I'm bad at naming functions especially for Project Euler problems?

Okay you might start with:

function sum_mul35_below(below=1000)
    sum = 0
    for i=1:below-1
        if i % 3 == 0
            sum += i
        end
        if i % 5 == 0
            sum += i
        end
    end
    return sum
end

You can of course copy it into your REPL and evaluate it but I think including the file is much easier.

include("euler_1.jl")

You run it with sum_mul35_below(16) to get 75 and maybe you think that's seems high and check: 3+5+6+9+10+12+15 is 60. Then you realize you've added 15 twice by jumping into both ifs and change your code to:

function sum_mul35_below(below=1000)
    sum = 0
    for i=1:below-1
        if i % 3 == 0 || i % 5 == 0
            sum += i
        end
    end
    return sum
end
julia> sum_mul35_below(16)
75

damn!

Gotya!

include("euler_1.jl")
julia> sum_mul35_below(16)
60

This might seem a bit tedious. Wouldn't it be nice if you don't have to type include again and again and again?

Guess what: Revise is there for rescue.

] add Revise
using Revise
includet("euler_1.jl")

Don't forget the t in includet ;)

So yeah I missed that in the package mode section as it's outside that mode: You use a package with using.

It also works when you develop a package where it is even more useful. For example

using Revise
using ConstraintSolver

doing some changes in ConstraintSolver and you can just test them in the REPL.

That's the reason why I think everyone should use that package ;)

A small note: The above code is not the fastest. Feel free to check out my Project Euler streams on Twitch or YouTube. I'm currently at problem 23 and stream for about 2 hours every Wednesday at 3PM UTC.

Would love to see you there as I might need some help with more complicated problems ;)

List of helpful resources

There is of course also StackOverflow but not much is going on there. Maybe I'm not the only one who thinks it's a toxic environment especially for newcomers...

Next up

There is a lot more to cover but this should be a good start for the basics in my opinion. On my list of posts in this series are things like:

If you're a beginner you might be interested in: Getting mentored

Thanks for reading and special thanks to my eight patrons!

List of patrons paying more than 4$ per month:

Currently I get 24$ per Month using Patreon and PayPal.

For a donation of a single dollar per month you get early access to the posts. Try it out at the start of a month and if you don't enjoy it just cancel your subscription before pay day (end of month).

I'll keep you updated on Twitter OpenSourcES as well as my more personal one: Twitter Wikunia_de



Want to be updated? Consider subscribing on Patreon for free
Subscribe to RSS