Creation date: 2020-07-01
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.
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:
?
for help mode
;
for shell mode
]
for package mode
You can simply type one of those characters to switch to the mode.
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 :)
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.
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 /home/ole/Julia/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
defines the direct dependencies and some bounds
Manifest.toml
defines all dependencies exactly
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
@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.
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
.
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 😉
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 😉
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 ;)
For questions which should remain for a longer period
For longer questions not suited for Chat based systems
The link brings you to the registration page
Awesome for short questions to get answers very fast
Chat based so sometimes your question gets overlooked if there are other question directly after yours
Introduce yourself in #introduction and questions go to #helpdesk
There are some more useful channels if you are interested in special topics
Similar to Slack but questions don't get overlooked based on their threaded system
Fewer people than in Slack
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...
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:
Useful macros without getting into detail on how they work
Packages with less than 100 stars which I find awesome
Debugger vs Infiltrator <- Already done 😉
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:
Site Wang
Gurvesh Sanghera
Szymon Bęczkowski
Logan Kilpatrick
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