By: Steven Whitaker
Re-posted from: https://glcs.hashnode.dev/learning-packages
Julia is a relatively new,free, and open-source programming language.It has a syntaxsimilar to that of other popular programming languagessuch as MATLAB and Python,but it boasts being able to achieve C-like speeds.
Julia comeswith a lot of functionality built-in.However, functionalitythat isn’t already built-inneeds to be createdfrom base Julia.Fortunately,Julia provides a simple, yet powerful, mechanismfor reusing and sharing code:packages.
Thanks to packages,we don’t have to write the codeto do many common tasks ourselves.(Imagine having to write a plotting function from scratch…)
We learned in a previous post about the Pkg REPL promptand how it can be used to install packages.
To install a package,you have to know it exists.And when using a packagefor the first time,how do you know where to begin?The purpose of this postis to address this question.
In this post,we will learn how to find useful packagesand demonstrate how to discovera package’s functionalityand learn how to use it.
This post assumes you already havea basic understanding of variables and functionsin Julia.You should also understand the differencebetween functions and methods.If you haven’t yet,check out our earlierpost on variables and functionsas well as our post on multiple dispatch,which explains the differencebetween functions and methods.
Package Discovery
The first step to learning a Julia packageis actually finding the package.
Essentially all Julia packages are registered,or made available for downloadvia the Pkg REPL prompt,in Julia’s General registry.Therefore, one could look through the registryto get a sense of the different packages available.
For a more powerful way to explore Julia packages,check out juliapackages.com.This website gathers information from GitHubto allow sorting by popularity(as measured by the number of GitHub stars)and when packages were last updated(which can help give a senseof how actively maintained or updated packages are).You can also explore packages by category.
Finally,another way to discover packagesis to visit Julia Discourse.You can look at package announcementsto see what packages are being created.You can also peruse the specific domains tagsto see what packages people are talking aboutand get a feel for what packages people usefor different applications.
Now that we have some toolsfor discovering packages,let’s discuss how to learnhow to use a package.
Learning Package Functionality
Look at Documentation
The first step to finding outwhat a package has to offeris to look at the package’s documentation.
Most packages will have at least a READMEthat will list package functionalityand provide some examplesof how to use the package.See Interpolation.jl’s READMEas an example.
Often, more established packageswill also have dedicated documentation(that typically is linkedin the README).Documentation typically includesmore in-depth examplesof how to perform specific tasksusing the package.For example,DataFrames.jl includes a “First Steps” pagein its documentation.
Another common feature of package documentationis a list of functions, types, constants, and other symbolsdefined by the package.See, for example,ForwardDiff.jl’s differentiation API.This list can be usefulfor discovering all possible package functionality,especially when the examples elsewhere in the documentationcover only a small portionof package functionality.
Explore in the REPL
Besides looking at online documentation,the REPL can also be usefulfor learning how to use a package.
After a package is loaded,an exhaustive list of symbolsdefined in a packagecan be obtained via tab completion:
julia> using Debuggerjulia> Debugger.<tab><tab>@bp @breakpoint @enter@make_frame @run DebugCompletionProviderDebuggerState HIGHLIGHT_24_BIT HIGHLIGHT_256_COLORSHIGHLIGHT_OFF HIGHLIGHT_SYSTEM_COLORS HighlightOptionLimitIO LimitIOException LineNumbersMAX_BYTES_REPR NUM_SOURCE_LINES_UP_DOWN RESETRunDebugger SEARCH_PATH WATCH_LIST__init__ _current_theme _eval_code_iscall _isdotcall _make_frame_preprocess_enter _print_full_path _syntax_highlightingactive_frame add_breakpoint! add_watch_entry!append_any assert_allow_step body_for_methodbreak_off break_on break_on_errorbreakpoint breakpoint_char breakpoint_linenumberscheck_breakpoint_index clear_watch_list! completionscompute_source_offsets disable_breakpoint! enable_breakpoint!eval execute_command get_function_in_module_or_Mainhighlight_code include interpret_variableinvalid_command julia_prompt locdesclocinfo maybe_quote parse_as_much_as_possiblepattern_match_apply_call pattern_match_kw_call print_codeinfoprint_frame print_lines print_localsprint_next_expr print_sourcecode print_statusprint_var promptname remove_breakpoint!repr_limited set_highlight set_themeshow_breakpoint show_breakpoints show_watch_liststacklength suppressed toggle_breakpoint!toggle_lowered toggle_mode write_prompt
As discussed in ourpost about the Julia REPL,the help prompt can be usedto display documentationfor individual functions and types:
# Press ? to enter help modehelp?> filtersearch: filter filter! fieldtype fieldtypes filter(f, a) Return a copy of collection a, removing elements for which f is false. The function f is passed one argument.
If you want to find outwhat methods existfor a given function,you can use tab completion:
julia> print(<tab>print(io::IO, ex::Union{Core.GotoNode, Core.SSAValue, Expr, GlobalRef, Core.GotoIfNot, LineNumberNode, Core.PhiCNode, Core.PhiNode, QuoteNode, Core.ReturnNode, Core.Slot, Core.UpsilonNode}) @ Base show.jl:1384print(io::IO, s::Union{SubString{String}, String}) @ Base strings/io.jl:246print(io::IO, x::Union{Float16, Float32}) @ Base.Ryu ryu/Ryu.jl:128print(io::IO, n::Unsigned) @ Base show.jl:1144
You can also use the methods
function:
julia> methods(print)# 35 methods for generic function "print" from Base: [1] print(io::IO, ex::Union{Core.GotoNode, Core.SSAValue, Expr, GlobalRef, Core.GotoIfNot, LineNumberNode, Core.PhiCNode, Core.PhiNode, QuoteNode, Core.ReturnNode, Core.Slot, Core.UpsilonNode}) @ show.jl:1384 [2] print(io::IO, s::Union{SubString{String}, String}) @ strings/io.jl:246 [3] print(io::IO, x::Union{Float16, Float32}) @ Base.Ryu ryu/Ryu.jl:128 [4] print(io::IO, n::Unsigned) @ show.jl:1144
The methods
functionalso allows filteringon input typesand on the modulein which the methods are defined.For example,to get a list of methods of print
that take two arguments,the second of which is an AbstractChar
:
julia> methods(print, (Any, AbstractChar))# 5 methods for generic function "print" from Base: [1] print(io::IO, c::Char) @ char.jl:252 [2] print(io::IO, c::AbstractChar) @ char.jl:253 [3] print(io::IO, x) @ strings/io.jl:32 [4] print(io::IO, xs...) @ strings/io.jl:42 [5] print(xs...) @ coreio.jl:3
(See also the related methodswith
function.)
And to get methods of print
defined in the Dates
package:
julia> using Datesjulia> methods(print, Dates)# 4 methods for generic function "print" from Base: [1] print(io::IO, x::Period) @ Dates ~/programs/julia/julia-1.9.4/share/julia/stdlib/v1.9/Dates/src/periods.jl:48 [2] print(io::IO, t::Time) @ Dates ~/programs/julia/julia-1.9.4/share/julia/stdlib/v1.9/Dates/src/io.jl:55 [3] print(io::IO, dt::Date) @ Dates ~/programs/julia/julia-1.9.4/share/julia/stdlib/v1.9/Dates/src/io.jl:714 [4] print(io::IO, dt::DateTime) @ Dates ~/programs/julia/julia-1.9.4/share/julia/stdlib/v1.9/Dates/src/io.jl:705
As another example,suppose you have a DataFrame
and want to use the groupby
functionbut aren’t sure what other argumentsgroupby
expects.Tab completion (or the methods
function) can help:
julia> using DataFramesjulia> x = DataFrame(a = 1:3, b = rand(3));julia> gx = groupby(x, <tab>groupby(df::AbstractDataFrame, cols; sort, skipmissing) @ DataFrames ~/.julia/packages/DataFrames/58MUJ/src/groupeddataframe/groupeddataframe.jl:218
After running a package’s function,you might want to learn more aboutwhat the function returned.The typeof
functionreturns the type of its input,and fieldnames
returns a list of propertiesthat can be accessed:
julia> d = Dict("a" => 1, "b" => 2)Dict{String, Int64} with 2 entries: "b" => 2 "a" => 1julia> x = collect(d)2-element Vector{Pair{String, Int64}}: "b" => 2 "a" => 1julia> typeof(x)Vector{Pair{String, Int64}} (alias for Array{Pair{String, Int64}, 1})julia> fieldnames(typeof(x[1]))(:first, :second)julia> x[1].first"b"
Tab completion can also be usedto list properties:
julia> x[1].<tab><tab>first second
You can also see where in the type hierarchyan object’s type lieswith the supertype
function:
julia> supertype(typeof(x))DenseVector{Pair{String, Int64}} (alias for DenseArray{Pair{String, Int64}, 1})
Read Source Code
In addition to reading documentationand experimenting in the REPL,sometimes the best way to learn a packageis to read the source code directly.While that may seem daunting at first,remember that Julia is a high-level language,making it somewhat easy to read(at least after getting used to it).
There are two main benefitsof reading the source code:
- You get to see how the packagecreates and usescustom types and functions.
- Typically code is organizedin a logical manner,so you get to see what symbolslogically belong together.For example,a file that defines a typetypically will also defineconstructors for that typeand functions that operate on it.
If you see a function calland want to know what method will be called,the @which
command in the REPL can help.For example:
julia> using DataFramesjulia> df = DataFrame(a = 1:3);julia> @which hcat(df, df)hcat(df1::AbstractDataFrame, df2::AbstractDataFrame; makeunique, copycols) @ DataFrames ~/.julia/packages/DataFrames/58MUJ/src/abstractdataframe/abstractdataframe.jl:1608
Now we know the file and line numberwhere the hcat
methodthat acts on two DataFrame
s is defined,and we can look at the source codeto learn more about what the method does.
If you don’t have any objects to work withbut know the types of the inputs,you can use the which
method instead:
julia> which(hcat, (DataFrame, DataFrame))hcat(df1::AbstractDataFrame, df2::AbstractDataFrame; makeunique, copycols) @ DataFrames ~/.julia/packages/DataFrames/58MUJ/src/abstractdataframe/abstractdataframe.jl:1608
Summary
That wraps up our discussionabout how to find useful packagesand how to discover and learn to usea package’s functionality.We listed a few tools for finding packagesand walked through some different methodsfor learning how to use a package,including looking at documentation,exploring in the REPL,and reading source code.
Do you have any tips or tricksfor learning how to use packages in Julia?Let us know in the comments below!
Now that you have a better ideaof how to learn Julia packages,move on to thenext post to learn about parallel processing in Julia!Or,feel free to take a lookat our other Julia tutorial posts.
Additional Links
- Julia for Analysts: Tips for Better Beginnings – Embrace the REPL (#3)
- Blog post with some more tips for getting help(see the heading “Getting help without leaving my workflow (no more googling)”).