Delving into Open Source Packages for Julia

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.

Screenshot of juliapackages.com

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.

Picture of an open book

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 printthat 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 printdefined 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 DataFrameand 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 fieldnamesreturns 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})

Picture of source code

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:

  1. You get to see how the packagecreates and usescustom types and functions.
  2. 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 DataFrames 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