julia> using JuliaCon, Distributed
julia> @everywhere Juliacon2022().today()
This workshop will be a jam-packed, hands-on tour of the Julia REPL so that beginners and experts alike can learn a few tips and tricks. Every Julia user spends a significant amount of coding time interacting with the REPL. My claim for this workshop is that all Julia users can save themselves more than 3 hours of productive coding time over their careers should they attend this workshop, so why not invest in yourself now?
- Download Julia from julialang.org.
- Click/open the executable to pull up the
JuliaREPL
- If you need colors (like I do), type this in:
julia> using Pkg;
julia> using Pkg.add("OhMyREPL")
julia> using OhMyREPL
You should be good to go!
A less hands-on (but valuable reference) for the information inside this tutorial is in the Julia REPL manual. "Tightening" the feedback loops to find help in
- the manual
- inside the Julia REPL itself
- with others
- and online will likely be worthy investment to those that seek to master the Arts of Julia. Don't underestimate mechanical fluency!
Happy hacking!
We're going to start with the very basics. It's a good idea to think of this as musical piano scales - we're going for examples to trigger your muscle memory.
julia> x = 3+3
6
Notable points: You can copy/paste
input from anywhere into a Julia REPL. Try it now, to make sure you don't need to erase the julia>
.
Now, type in
julia> ans
And notice the ans
evaluates to the previously evaluated value. This is extremely useful, and can save your bacon when not re-running expensive calculations.
This is ~about as much mileage as you can extract from many other REPL in many other languages - but we should be greedy, because this is Julia, and we expect the best in interactive and performant computing.
We will now squeeze the most out of our Julia mode
of this REPL. Type the following into the REPL:
for i in 1:3
parse(OOPS, "123")
end
The manual invites you to try the following:
Ctrl+D
, to quit (also written as^D
Ctrl+C
will interrupt/cancel ongoing actionsCtrl+L
will clear your entire screen (I spam this so much - I love it.)Alt+Enter
will let you type in a newline without evaluating the REPL result. Try the following:- Type the
UP
Arrow to get to the definition ofx
. Then pressEnter
. This scrolls through your REPL history. - Get back to a previous definition in your REPL, press
Enter
, and thenDown
. You should notice that you can "get back" to where you started evaluating. This is very useful for when you have selected the "good REPL history" and you can begin a presentation there.
- Type the
- Moving around
Moving around with arrow keys is fine, but there's so much more power to learning to move around without resorting to the keys. Try moving around only through the
X
s in the following string:
# Try and make it to the apple!
snek = """
๐XXXXX XXXX
X XXXX X X
X X X X X
X X XXXXXX X
XXXXX X
X๐
""";
Remember:
- Up:
Ctrl+P
(Memorization tip:P
is forPrevious
) - Down:
Ctrl+N
(Next
) - Left:
Ctrl+B
(Back
) - Right:
Ctrl+F
(Forward
)
Now try:
function<CTRL+P>
That means that you should type in Ctrl
and P
at the same time, just after you have typed in function
, no spaces.
You should have scrolled "back in time" through your REPL history and hit the first matching previous time you typed in function
. (The same works with UP
arrow, btw.)
This trick let's you veeeeery quickly find stuff in your looooong REPL history if you kinda sorta remember what something was called.
If you liked this trick, please spam your favorite emoji in the Youtube/Twitch/Discord chat and @miguelraz
on twitter with the #JuliaCon2022
hashtag.
We're gonna build the hype :D.
Back to movement exercises.
Try moving through the four courners of snake box but now with the more powerful
meta+F
(word Forward)meta+B
(word Backward)Ctrl+A
/Home
(At Home, beginning of line)Ctrl+E
/End
(go to End of line)
Once you've tried a few iterations of the snake and gone back and forth a bit, please ask a question.
No, you can't delete your variables in Julia (it makes the compiler and Jeff Bezanson sad). But you can see what has been defined! Try
varinfo()
This should show you the variables that are defined and how much they "weigh" in memory.
ans
is bound to whatever you evaluated lastBase
is where all the batteries-included functionality is preparedCore
is the place where Julia starts itself up from - we don't need to worry about it.
Try defining some new variables and seeing how much they weigh in memory. which of these is lightest?
a = 1:1e6
b = collect(1:1e6)
We've already seen a few Ctrl
s here and there, but there's a few more where you might not have expected them. Again, please do read the manual and try them out (even if they are classic Emacs key chords, I know.)
meta+<
/meta+>
change to your first/last entry in this REPL sessionmeta+Backspace
delete the previous word, like the followingMushrooms
Ctrl+Right
/Ctrl+Left
will skip over an entire word
words = """
Badger Badger Badger Mushroom
Badger Badger Mushroom Badger
Badger Mushroom Badger Mushroom
""";
or the spurious TODO
here:
function hello(x)
println(TODO"Yes hello $x)
end
You can also use
meta+d
to delete the forward
word
And also use
Ctrl+W
/^W
to deleteW
hitespace in the following functions
3 + "banananananannananananananananananananannanana" 3
OOPS_GARBLED_INPUT simple(x) = x^2
(BTW, have you tried using Ctrl+Right
/Ctrl+Left
when going through an expression? It's not a bad backup...)
Try this now:
julia> @edit @less
You should get a gnarly stack trace.
Now type in 2+Ctrl+Q
(beware of some of your shortcuts exiting your terminal window.)
Your normal IDE/Editor of choice should have shown you where the funciton that is defined. If this didn't work, make sure to set
ENV["JULIA_EDITOR"] = "/usr/bin/vim"
Env # Hmmmm... I wonder if there's an easier way to explore all this input...
or whatever floats your โต. This configuration should persist between Julia sessions. (Choosing a lightweight IDE that doesn't spin up a Julia LSP session upon every invocation is good for ninja editing files and staying nimble).
Flow tip:
Get an error -> Jump into the stacktrace of your package -> exit your editor -> re-run your code
We'll talk a bit more about Julia configuration in a bit, stay tuned! We're almost done with the gnitty exercises.
Pro tip: Whenver you see pretty formatted outfit like the stack traces, you should be able to jump to it with Ctrl+Q
as well.
Try this:
methods(parse)
And jump to one of them.
When starting with Julia, it can feel very big.
What the heck is parsing? Bootstrapping? A compiler? That funky dragon in the LLVM logo that haunts my dreams?
You're about to learn a very powerful tip: Just about everything you can touch in the Julia REPL expands/auto-completes when you use the amazing <TAB>
. Try it!
using Lin<TAB>
par<TAB>
parse(<TAB>) # Shows all methods
parse(Int, <TAB>) # Narros to all methods that start with Int!
fi<TAB>
String.<TAB>
lines = readlines("file<TAB>")
parse(Int, <TAB>)
\alpha<TAB>
\:boat:<TAB> |> clipboard
; cd ~/.julia/<TAB>
... and you can setup your own latex shortcuts (Hat tip to Keno ๐ค !
using REPL
REPL.REPLCompletions.latex_symbols["\\pol"] = "`โโ" # instead of \partial<tab>\overl<tab>a<tab><tab>`
...and many more.
This little nugget of wisdom, judiciously used will let you quickly get a grasp of what's available in a library that you just imported - functions, fields, types, etc. For example, use it to find all macros defined in Base
:
@<TAB>
Let's power up our search by using fuzzy-finding in reverse mode with:
Ctrl-R
/Ctrl-F
(Reverse/Forward)
Cool, huh?
Now let's keep going.
Run this line:
# Horrible, horrible Julia ๐คฎ
function hamming(s1, s2)
counter = 0
length(s1) == length(s2) || return -1
for i in 1:length(s1)
if s1[i] != s2[i]
counter = counter + 1
end
end
return counter
end
- Place your cursor
๐ฒ
at the beginning of thefor
loop. - Now press
Ctrl-Space
- to mark a spot - Move your cursor to the
return counter
line and pressCtrl-X
(EX
change)
A couple of times to jump back and forth from where you where. This is useful once your functions start getting longer and longer.
Next drill: Deleting
Run this line:
# Horrible, horrible Julia ๐คฎ
! function hamming(s1, OOPS s2)
counter = 0 OOOOOOOOOOOOOPS # Ctrl+K
for i in ! 1:length(s1) # Ctrl+D
if s1[i] OOPS != s2[i] # Ctrl+W
OOPS counter = counter = 1 # Ctrl+W
OOPS OOPS OOPS OOPS # Ctrl+K
end
OOPS end
return COUNTER # meta+l
length(s1) == length(s2) || erturn -1 # Ctrl+T, then meta-Up
OOPS end # meta-Left
It seems a very dedicated cat ๐ ran across our keyboard (and maliciously inverted the length check???). The following should be useful for getting rid of may of those mistakes:
Ctrl+/.
/Ctrl+_
to undo our previous actions (Help? Can't get this to work?)Ctrl+K
willK
ill to the end of the line and place it in thekill ring
/clip boardCtrl+D
will delete one character and aD
vance the cursorCtrl+W
will delete previous text going up to whitespacemeta+l
will lowercase a wordCtrl+T
willT
ranspose the characters about the cursor, use it on the malformedbreak
meta+Up
will "float" the entire line up to where it's supposed to gometa-left
to re-indent the last line appropriately
TODO: Master the kill rings...
The manual specifies how to setup your own keybindings. Here's what a "minimalist" vim profile would look like for me:
import REPL
import REPL.LineEdit
const mykeys = Dict{Any,Any}(
# Up
"^k" => (s,o...)->(LineEdit.edit_move_up(s) || LineEdit.history_prev(s, LineEdit.mode(s).hist)),
# Down
"^j" => (s,o...)->(LineEdit.edit_move_down(s) || LineEdit.history_next(s, LineEdit.mode(s).hist))
)
function customize_keys(repl)
repl.interface = REPL.setup_interface(repl; extra_repl_keymap = mykeys)
end
atreplinit(customize_keys)
but you can find oodles more actions in the LineEdit.jl
However, Julia ships with a much more structured way to include "helpful" information along code, and that can be done by pressing ?
via the help mode.
At time of writing, there are 4 standard Julia REPL modes:
- Julian mode
- Help mode, with
?
- Pkg mode, with
]
- Shell mode, with
;
The king of kings. Instead of needing to open a browser window for every
Try the following:
?parse
Your prompt should now look like ?>
and turn yellow.
When you define a function, you can also define a docstring
by writing a string comment just above it.
That string gets rendered as Markdown within the terminal.
Try this
" Super helpful string comment"
hamming(s1, s2) = mapreduce(!=, +, s1, s2)
or this
@doc "Also clever insight" hamming
But there's many kinds of "markup" that the Julia REPL allows. Try adding:
- a header
- a note
- a compat warning above Julia 1.6
- julia examples
- bolded text
- a part that says
# Extended help
and then running??parse
๐ ?DataFrames
- if you don't have a docstring for your main Module, it will just display your repo'sREADME.md
.
(Hint: Look at ?parse
and try and find all the doc comments with @edit parse(Int, "123")
)
Interestingly, Julia has a particular flavor of documentation because of multiple dispatch - when new methods are defined for old types, those get also added to the documentation.
Try this
- add another docstring to
parse
in a new module to a local type and see that the docs get updated
You can also use the apropos("Unicode")
function, or ?>"Unicode"
to get a dump of all function docstrings that contain the string "Unicode".
If you know some regular expressions, you can also ask with those!
Try this:
?r"arg[0-9]"
To find all docstrings that contain arg
followed by a number.
Try this
julia> 'c'
Julia will kindly show you the Unicode character representation of whatever character you input.
Try it with emojis!
Try this:
?> โ
"โ" can be typed by \partial<tab>
This is called reverse latex/emoji lookup! If you come across a weird symbol, you can just ask Julia how to type it.
This is a "fake" shell mode - ideally best for jumping around directories with cd ~/.julia
and cleaning up git
stuff really quick.
Try this:
- Go into a Package you own
- type
; vim README.md
or your favorite editor - Fix a typo, save and quit
- Commit the file and push to the repo.
I said "fake" because it's not really a shell - it's an emulated version run by Julia (see the code here.
Try this ๐:
shell> echo $(readdir())
You can, however, run Julia code "inside" that emulated shell. Neat, huh!
Alright, now for the real productivity booster: The Pkg REPL. There's many more configs than what I will talk about, but these will get you 95% of the way there as a confident Julia user. You can checkk all the commands in the Pkg.jl manual
]add X Y Z
will addX Y Z
packages. Try this:
]add Diff<TAB>
It will autocomplete with matching package names in the registry!
Try this when developing a package
]test X
or just ]test
. This will run all the tests under tests/runtests.jl
and include some really nice output. This is vital to knowing that other's people's code works on your computer!
Try this:
]status
You'll get an info dump of all the packages and the version that they are at in the active environment.
To start an environment, do this:
]activate --temp
(or ]activate --temp
if you want a quick sandbox to debug/setup a MWE!). You will see you prompt change to MyPkg>
in blue.
This now makes all your installations only valid for the current environment!
Congrats! You have now graduated from the I-don't-have-to-spend-more-time-fighting-pip-for-the-rest-of-my-life academy!
If you want to eagerly download all the dependecies of an environment,
]instantiate
Will pull them in.
]dev YourPackage
Will start a new "development" version of a package that you can locally make changes to in ~/.julia/dev
- so you don't have to mess with your installed packages either!
Small tip - if you've been running Julia for a while (a few versions now), try running
]gc
To get rid of a bit of cruft you may have accumulated.
- workflow demos for contributing code fixes
- Revise.jl
- REPLMaker.jl BuildYourOwnMode demo,
-
Cross language comparison of REPL features (Cultural aside: using
grep
,bat
andtree
andgit
:) -
terminals and font support
-
startup file options
-
prompt changing
-
Easter eggs in the chat? ๐ฅ ๐ ๐
-
flag configurations (quiet, banner)
@code_*
BenchmarkTools.jl
- OhMyREPL.jl,
- TheFix.jl
- Latexify.jl
using Latexify
arr = ["x/y" 3//7 2+3im; 1 :P_x :(gamma(3))]
latexify(arr)
- PkgTemplates.jl
t = Template(user = "miguelraz")
t("MySuperPackage")
- Eyeball.jl
using Eyeball
a = (h=rand(5), e=:(5sin(pi*t)), f=sin, c=33im, set=Set((:a, 9, rand(1:5, 8))), b=(c=1,d=9,e=(i=9,f=0)), x=9 => 99:109, d=Dict(1=>2, 3=>4), ds=Dict(:s=>4,:t=>7), dm=Dict(1=>9, "x"=>8))
eye(a)
- TerminalPager.jl,
julia> rand(100, 100) |> pager
julia> pager(rand(100, 100))
- AbstractTrees.jl,
julia> print_tree(FloatTree(NaN, [FloatTree(Inf, []), FloatTree(-Inf, [])]))
NaN
โโ Inf
โโ -Inf
- Debugger.jl / Cthulhu.jl / SnoopCompile.jl
using Debugger
@enter parse(Int, 123)
- UnicodePlots.jl
- ProgressMeters.jl
using ProgressMeter
@showprogress 1 "Computing..." for i in 1:50
sleep(0.1)
end
- Term.jl
- PrettyTables.jl
- PlutoREPL.jl (???) project
juliaup
: Be comfortable switching between channels!- TerminalMenus.jl in base
- Terminal User Interfaces in Julia by Dheepak Krishnamurthy
- VideosInTerminal.jl + ImagesInTerminal.jl like Jesse Betancourt's
julia> using VideoInTerminal
julia> framestack = map(i->rand(Gray{N0f8},60,40), 1:200); # a vector of images of the same type and dims
julia> play(framestack) # play through the framestack
julia> colorcube = rand(Gray{N0f8},60,40,30);
julia> play(colorcube, 2) # play slices along dim 2
julia> play("path/to/video.mp4")
And if you wanna try out something kinda funky...
julia> showcam
- PortAudio.jl - I mean, we might as well record audio now, right?
julia> import LibSndFile # must be in Manifest for FileIO.save to work
julia> using PortAudio: PortAudioStream
julia> using SampledSignals: s
julia> using FileIO: save
julia> stream = PortAudioStream(1, 0) # default input (e.g., built-in microphone)
julia> buf = read(stream, 5s)
480000-frame, 2-channel SampleBuf{Float32, 2, SIUnits.SIQuantity{Int64,0,0,-1,0,0,0,0,0,0}}
10.0 s at 48000 sโปยน
โโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
julia> close(stream)
julia> save(joinpath(homedir(), "Desktop", "myvoice.ogg"), buf)
- DoctorDoctrings.jl and hijacking REPL history
- BinaryBuilder.jl
using BinaryBuilder
BinaryBuilder.run_wizard()
-
InteractiveErrors.jl
-
AbbreviatedStackTraces.jl
-
pkg prompt with temp directory - incredibly useful for debugging/setting up MWE
-
] add Foo; undo
! -
Jacob Quinn: ?foo gives you help, but ??foo gives you the stuff under "# Extended help" https://julialang.slack.com/archives/C6FGJ8REC/p1623860727294500
-
LLVM_JULIA_ARGS=-time-passes ./julia -e 'using Plots; plot(1:10)'
-
Change your prompt:
-
] activate @juliaimages
โ โ
โโโ โโโโ
โโโโ โโโโโโ
โโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโ
- The REPL has Emacs-y bindings by default - Can you try adding Vim keybindings? Normal editing is a more mechanical PR. Composing the vim keys is where it's interesting!
- Can you get regular expressions to dump into the terminal as you can? Try this for inspiration.
- Data structure graphs would also be amazing to have at the Julia terminal - anyone wanna give it a shot?
@which parse(Int, "123")
doesn't print with regular highlighting- Alright,
<TAB>
autocompletes, but why doesn't it show me colors of functions/Structs/Modules/vars/constants in different formats when I doLinearAlgebra.<TAB>
? Can you do it? - "terse"
]test
mode flag that doesn't print all the Pkg deps and status? - switch repl modes via menu JuliaLang/julia#33875
- Why is the shell mode not better documented? Send a PR!