Author Archives: Justyn Nissly

Maximizing Julia Development with VSCode Extension

By: Justyn Nissly

Re-posted from: https://blog.glcs.io/julia-vs-code-extension

In this article, we will review some of the features that the Julia extension offers! If you don’t have Julia, VS Code, or the Julia extension already installed, look at this article to help you get set up!

Running a Julia File

Now that we have the extension installed and configured, we can start using the extension.One of the first things we will examine is the most basic feature – running code!To run code, we do the following:

  1. Click the drop down in the top right of the code window
  2. Click “Julia: Execute Code in REPL”
  3. Enjoy the results!

When you hit Ctrl+Enter, the line your cursor is currently on will run, and the cursor will advance to the next line.This is one way to step through the code line by line.

You are also able to use Ctrl+F5 (Option+F5 for Macs) to run the entire file. You can learn more about running Julia code from the official documentation.

Code Navigation

The information in this section applies to any language and is not exclusive to the Julia extension. The features below are helpful to know and can increase your productivity as you code.

Within VS Code, if you use Ctrl+P, a prompt will open at the top of your editor. In that prompt, you can start typing the name of a file you want to jump to in your project. Once you click the option (or press enter), VS Code will jump directly to that file. You can also use Ctrl+G to jump to a specific line number in the file if you know which line you are looking for. Being able to jump back and forth between files without having to search the file tree will greatly enhance your workflow. Imagine all the time you will save by not searching through file trees!

Using Ctrl+Shift+O (be sure to use the letter O, not the number 0) will allow you to navigate through individual symbols within your program. After using the shortcut above, type : and all your symbols will be grouped by type. You are then able to navigate between them all.

Editing Code

Code navigation is a great skill to develop, especially when given some wonderful legacy code to unravel…we’ve all been there. Being proficient in navigating your code does you no good unless you are also proficient at editing the code.

One way to get going quickly is to use the “rename symbol” shortcut. You can either right click on a symbol and press “rename” or hit F2. When you rename the symbol, it will be renamed everywhere else in the file that it exists. Pretty neat, huh?

Change_Symbol

The Plot Viewer

Up until this point in the article we have laid the ground work for working with your code in VS Code. Next, we will look into some of the Julia specific features that the Julia extension offers, starting with the plot viewer.

The plot viewer is a really handy tool that lets you…well…view plots. We can look at an example to see how it works.

First, we will install the plots package if it hasn’t been installed already.

julia> using Pkgjulia> Pkg.add("Plots")# OR# We can type the "]" key and use the package interface(@v1.10) pkg>(@v1.10) pkg> add Plots

After we do that, we can create a plot to visualize.

using Plotsfunction make_plot()    # Create a plot    p = plot()     = range(0, 2, length=100)    x = cos.() * 5    y = sin.() * 5    plot!(p, x, y, linecolor=:black, linewidth=2, legend=:topright)    x_left = 1 .+ 0.5 * cos.()    y_left = 2 .+ 0.5 * sin.()    plot!(p, x_left, y_left, linecolor=:black, linewidth=2, fillalpha=0.2)    x_right = 3 .+ 0.5 * cos.()    y_right = 2 .+ 0.5 * sin.()    plot!(p, x_right, y_right, linecolor=:black, linewidth=2, fillalpha=0.2)    _arc = range(0, , length=10)    x_arc = 2 .+ cos.(_arc) * 2    y_arc = -1 .+ sin.(_arc) * 1    plot!(p, x_arc, y_arc, linecolor=:black, linewidth=2)    # Adjust plot limits and display the final plot    xlims!(-6, 6)    ylims!(-6, 6)    display(p)end# Execute the function to plotmake_plot()

Next we run this by using the keyboard shortcut we learned earlier (Ctrl+Enter), and we can see the result below!

Make_Plot

Pretty cool! Now we can see our charts generated in real time right inside our editor!

The Table Viewer

I don’t know about you, but I never liked having to try and dump the contents of an array, matrix,or other data structures to the console and try and parse through the data. Lucky for us we don’t have to do that.The Julia extension allows us to view any Tables.jl compatible table in the special table viewer.There are two ways to do this.

The first way is by clicking the “View in VS Code” button next to your table in the “Workspace” section.

Table_View_Button

The second way to do this is by running the vscodedisplay(name_of_table) directly in the REPL.

It’s pretty cool if you ask me.Having the ability to view the data is a nice feature, but to make it even better, you can sort the data in the UI by clicking the column headers. You can also copy data by using Ctrl + C just like you would in Excel!

A word of caution: All the table data you see is cached so any changes you make will not be reflected in the table viewer. To fix that just re-display the table in the editor and you will see your changes.

Debugging

The last topic we will cover is the debugging tools.

There are many things you can do in the VS Code debugger that are not language-specific. We aren’t going to cover all of those features here, so if you want a more in-depth look, check out the official documentation.

Since the only truly bug free code is no code, we will start by writing some code that we can test and try to catch bugs in.

function do_math(a, b)    c = a + b    d = a * b    c + dendfunction print_things()    println("First print")    println("Second print")    var = do_math(5,8)    println("Third print and math: ", var)    var2 = do_math("Bug",3)    println("Fourth print and bug: ", var2)endprint_things()

We have code to run, so we can run the debugger and see what we get. First, we must switch to the “Run and Debug” tab. We do this by either clicking on the tab (the one with the bug and play button) or by hitting Ctrl+Shift+D.

Once we are there, we will be greeted with a screen like this:

Debug_Screen

From here we can observe the compiled Julia code, our breakpoints, and several other things as the program runs. We will want to run our code through the debugger, and to do that, we can either click the big “Run and Debug” button or hit F5.

Debug_Error_Message

We step through the code a bit, and see some of what the debugger will show us.

Debugger_Annotated

1: The variables passed into the function

2: The variables local to the function

3: The indicator of which line we are currently on

4: A breakpoint indicator

We can set our breakpoints by double clicking next to the line numbers. After setting our breakpoints, we will be able to step through the code. As we step through the code line by line, the variables that get created are populated in the top section and their values are shown. Another neat feature the debugger gives that is not highlighted above but should be noted is the “CALL STACK” section. As the name suggests, this will show us the entire call stack as you step through the code. All of these are most likely things we have seen before in other debuggers, but they are useful nonetheless.

Keyboard Shortcuts

To wrap up, let’s look at a list of keyboard shortcuts for effective Julia extension usage.Note that a command like Alt+J Alt+C means press Alt+J followed by Alt+C.

Command Shortcut
Execute Code in REPL and Move Shift+Enter
Execute Code in REPL Ctrl+Enter
Execute Code Cell in REPL Alt+Enter
Execute Code Cell in REPL and Move Alt+Shift+Enter
Interrupt Execution Ctrl+C
Clear Current Inline Result Escape
Clear Inline Results In Editor Alt+J Alt+C
Select Current Module Alt+J Alt+M
New Julia File Alt+J Alt+N
Start REPL Alt+J Alt+O
Stop REPL Alt+J Alt+K
Restart REPL Alt+J Alt+R
Change Current Environment Alt+J Alt+E
Show Documentation Alt+J Alt+D
Show Plot Alt+J Alt+P
REPLVariables.focus Alt+J Alt+W
Interrupt Execution Ctrl+Shift+C
Browse Back Documentation Left
Browse Forward Documentation Right
Show Previous Plot Left
Show Next Plot Right
Show First Plot Home
Show Last Plot End
Delete plot Delete
Delete All Plots Shift+Delete

Summary

We reviewed some of the basics of the Julia VS Code extension. We looked at running a Julia file, the basics of code navigation and editing, the usefulness of the plot and table viewers, and some basic debugging features. This was only an overview, and there is much more that the extension has to offer! If you would like to do a deeper dive into the Julia VS Code extension, visit the official documentation or their GitHub.

If you would like to learn more about Julia so you can fully take advantage of the extension’s features, check out our Julia Basics series!

]]>

Delving into Advanced Types within the Julia Type System

By: Justyn Nissly

Re-posted from: https://blog.glcs.io/julia-types-advanced

In a previous post we covered the building blocks of the Julia type system and discussed just how powerful it can be.

What if I were to tell you that Julia’s type system has even more to offer?Well, welcome back to class, my friends! Today, we will see what else the Julia type system offers us. Let’s dive right in and look at our first topic: Type Unions.

Type Unions

Type unions are exactly what you think they are. They are a union of two or more types that create a new abstract type.With this new abstract type that you create with the Union keyword, you can assign values to a variable with that type that matches any of the types specified in the Union.Let’s look at an example:

    julia> IntOrString = Union{Int,AbstractString}    Union{Int64, AbstractString}    julia> function testUnionTypes(aType::IntOrString)            print(`The type of aType is $(typeof(aType))`)        end    testUnionTypes (generic function with 1 method)    julia> testUnionTypes(1)    `The type of aType is Int64`    julia> testUnionTypes("Testing Union Types")    `The type of aType is String`    julia> testUnionTypes(3.14)    ERROR: MethodError: no method matching testUnionTypes(::Float64)    Closest candidates are:    testUnionTypes(::Union{Int64, AbstractString})    @ Main REPL[8]:1    Stacktrace:    [1] top-level scope    @ REPL[11]:1

You’ll notice that when we test our new Union type, it rejects the float value we gave it, but it accepts both the integer and the string! Just like the Any type we discussed in a previous article on types, this allows us to develop generic code that works with several types. Julia itself actually uses this concept to allow for nullable types. Julia uses Union{T, Nothing} where T has any type you want. For example, Union{AbstractString, Nothing} would allow you to assign a string or a null (written with the symbol nothing in Julia).

A generic triangle

Parametric Types

Parametric types are a very interesting part of Julia, and yes, they are exactly what they sound like. They are types that can take parameters. Let’s look at an example of the syntax and then discuss its use.

julia> struct Triangle{T}           a::T           b::T       endjulia> myFloatTriangle = Triangle{AbstractFloat}(1.0,2.0)Triangle{AbstractFloat}(1.0, 2.0)julia> myFloatTriangle.a1.0julia> myFloatTriangle.b2.0

Now that we have created a parametric type, let’s see how we can use it.

julia> function calculate_hypotenuse(myShape::Triangle)           println("The hypotenuse is: $(sqrt(myShape.a^2 + myShape.b^2))")       endjulia> calculate_hypotenuse(myFloatTriangle)The hypotenuse is: 3.605551275463989julia> myIntTriangle = Triangle(1,2)Triangle{Int64}(1, 2)julia> calculate_hypotenuse(myIntTriangle)The hypotenuse is: 3.605551275463989

In our example above, we defined our “Triangle” variable in two ways.In one, we specified the type; in the other, we simply gave the values for the parameters.Julia was able to figure out the rest. This is all possible because T can be any type we give it.We don’t have to specify the type upfront, so we can make our code much more flexible and reusable. In fact, not specifying the type (as we did with myIntTriangle) is the preferred way to do it in Julia.


Tuple Types

You can think of Tuples more like boxes that hold items.After you put the items into the box, the box is “sealed,” and those items are set in that order with those values…FOREVER…Okay, maybe that is a bit dramatic, but I think you get the point.Tuple types are immutable containers with any number or combination of types.Let’s look at the syntax of Tuples:

julia> typeof((42,"Don't panic",10.9))Tuple{Int64, String, Float64}

One primary use for this is returning multiple types from a single function. Because Tuples are immutable, they ensure that the data stays structured in the order you tell it and remains constant. You can see several more examples of how Tuples are used in the Julia documentation or if you want a great explanation of how Tuples work, check out this video by DoggoDotJl. DoggoDotJl does a fantastic job explaining several different parts of the Julia language, and I highly recommend you check him out at DoggoDotJl on Youtube. #notsponsored

Named Tuples

Named Tuples are just Tuples…but with names. I bet you didn’t see that coming.Well, okay… Named Tuples are a little more than just Tuples with names.Let’s look at the syntax and then talk a little more about it:

julia> tupleWithNames = (           language = "Julia",           isTheBest = true,           type = "NamedTuple",       )(language = "Julia", isTheBest = true, type = "NamedTuple")julia> typeof(tupleWithNames)@NamedTuple{language::String, isTheBest::Bool, type::String}

A NamedTuple functions like a JSON object in that you have key-value pairs. They are a fantastic data structure to use when you want to organize your code but don’t need the flexibility of an array. When you want to access the elements of a NamedTuple you can do it in two different ways.

julia> function testingTuples(ourTuple)            println("We can use $(ourTuple.type) to check if $(ourTuple.language) is better than Python")            println("Is $(ourTuple.language) the best? $(ourTuple[2] ? "Yes it is! .jl > .py" : "No, use Python noob")")endtestingTuples (generic function with 1 method)julia> testingTuples(tupleWithNames)We can use NamedTuple to check if Julia is better than PythonIs Julia the best? Yes it is! .jl > .py

You can see in the above example that Julia is indeed better than Python! Okay, maybe this example proves nothing about Julia’s superiority and may need to be the topic of another post. But this example does show how you can access elements inside NamedTuples. You can access elements in tuples using the tuple name, the . operator, and then the name of the element you want to access. You can also access it by indexing. Indexing is also how to access data in Tuples that weren’t special enough for you to give names to. Now, with access to Tuples and NamedTuples, you can organize data in the same way you would with JSON objects.

NOTE: Just like Tuples, NamedTuples are immutable. Once the values are set, they cannot be changed later.

Picture of a cool lizard

UnionAll Types

Now I know what you are probably thinking: What is up with the lizard?That can be explained with two simple points:

  • Lizards are cool.
  • I like to compare UnionAll types to chameleons.

Now, let me elaborate on that a bit. Just like chameleons change color, UnionAll types can change form and adapt to any data type. Chameleons don’t change their color to yellow and suddenly become bananas. They are still chameleons. Similarly, you may have a UnionAll type that is a float in one instance and an integer the next, but it still has the same structure. Enough with the lizard talk; let’s look at a more practical example to make sense of this reptilian elucidation.

julia> abstract type Shape{T} endjulia> struct Circle{T} <: Shape{T}           radius::T       endjulia> struct Square{T} <: Shape{T}           side::T       endjulia> circle_float = Circle(2.5)Circle{Float64}(2.5)julia> square_int = Square(4)Square{Int64}(4)julia> square_int isa Shapetruejulia> square_int isa Squaretruejulia> square_int isa Circlefalse

You can see that we created an abstract type called Shape, and then we created two other types called Square and Circle. By doing this, we have created a universal way to handle shapes without tying ourselves to any specific numeric type. We aren’t limited to just integers or just floats. UnionAll types, similar to other topics we covered, allow you to build very flexible code that is type-stable, generic, and optimal for machine code generation.

Type Aliases

The last thing we will cover is type aliases.Julia allows type aliases to give a new name to an existing type.The idea here is the same as many other concepts we discussed: generic and reusable code.Take Int, for example. When you specify a number as an Int, Julia will check if it needs to be an Int32 or Int64,depending on your system. The Int alias is a convenience operator, removing the abstraction of 32-bit or 64-bit values. You specify a variable as Int, and Julia takes care of the rest.We should probably note that Julia does not have Float as an alias for specific size floating point numbers (such as Float64).Int reflects the exact size of the pointer native to the machine you are running your code on, whilefloating points, on the other hand, are specified by the IEEE-754 standard. So, just like in real school, you have some homework to do later…or not. I won’t judge.

Summary

To recap, we have covered Type Unions, Parametric Types, Tuple Types, UnionAll Types, and Type Aliases.Hopefully, you have a better understanding of the Julia type system and the power it wields.If you want to learn more about Julia or some other interesting topics,be sure to check out our other blog posts on blog.glcs.io!

Additional Links

Background for article cover image by Freepik

Delving into Advanced Types within the Julia Type System

By: Justyn Nissly

Re-posted from: https://glcs.hashnode.dev/julia-types-advanced

In a previous post we covered the building blocks of the Julia type system and discussed just how powerful it can be.

What if I were to tell you that Julia’s type system has even more to offer?Well, welcome back to class, my friends! Today, we will see what else the Julia type system offers us. Let’s dive right in and look at our first topic: Type Unions.

Type Unions

Type unions are exactly what you think they are. They are a union of two or more types that create a new abstract type.With this new abstract type that you create with the Union keyword, you can assign values to a variable with that type that matches any of the types specified in the Union.Let’s look at an example:

    julia> IntOrString = Union{Int,AbstractString}    Union{Int64, AbstractString}    julia> function testUnionTypes(aType::IntOrString)            print(`The type of aType is $(typeof(aType))`)        end    testUnionTypes (generic function with 1 method)    julia> testUnionTypes(1)    `The type of aType is Int64`    julia> testUnionTypes("Testing Union Types")    `The type of aType is String`    julia> testUnionTypes(3.14)    ERROR: MethodError: no method matching testUnionTypes(::Float64)    Closest candidates are:    testUnionTypes(::Union{Int64, AbstractString})    @ Main REPL[8]:1    Stacktrace:    [1] top-level scope    @ REPL[11]:1

You’ll notice that when we test our new Union type, it rejects the float value we gave it, but it accepts both the integer and the string! Just like the Any type we discussed in a previous article on types, this allows us to develop generic code that works with several types. Julia itself actually uses this concept to allow for nullable types. Julia uses Union{T, Nothing} where T has any type you want. For example, Union{AbstractString, Nothing} would allow you to assign a string or a null (written with the symbol nothing in Julia).

A generic triangle

Parametric Types

Parametric types are a very interesting part of Julia, and yes, they are exactly what they sound like. They are types that can take parameters. Let’s look at an example of the syntax and then discuss its use.

julia> struct Triangle{T}           a::T           b::T       endjulia> myFloatTriangle = Triangle{AbstractFloat}(1.0,2.0)Triangle{AbstractFloat}(1.0, 2.0)julia> myFloatTriangle.a1.0julia> myFloatTriangle.b2.0

Now that we have created a parametric type, let’s see how we can use it.

julia> function calculate_hypotenuse(myShape::Triangle)           println("The hypotenuse is: $(sqrt(myShape.a^2 + myShape.b^2))")       endjulia> calculate_hypotenuse(myFloatTriangle)The hypotenuse is: 3.605551275463989julia> myIntTriangle = Triangle(1,2)Triangle{Int64}(1, 2)julia> calculate_hypotenuse(myIntTriangle)The hypotenuse is: 3.605551275463989

In our example above, we defined our “Triangle” variable in two ways.In one, we specified the type; in the other, we simply gave the values for the parameters.Julia was able to figure out the rest. This is all possible because T can be any type we give it.We don’t have to specify the type upfront, so we can make our code much more flexible and reusable. In fact, not specifying the type (as we did with myIntTriangle) is the preferred way to do it in Julia.


Tuple Types

You can think of Tuples more like boxes that hold items.After you put the items into the box, the box is “sealed,” and those items are set in that order with those values…FOREVER…Okay, maybe that is a bit dramatic, but I think you get the point.Tuple types are immutable containers with any number or combination of types.Let’s look at the syntax of Tuples:

julia> typeof((42,"Don't panic",10.9))Tuple{Int64, String, Float64}

One primary use for this is returning multiple types from a single function. Because Tuples are immutable, they ensure that the data stays structured in the order you tell it and remains constant. You can see several more examples of how Tuples are used in the Julia documentation or if you want a great explanation of how Tuples work, check out this video by DoggoDotJl. DoggoDotJl does a fantastic job explaining several different parts of the Julia language, and I highly recommend you check him out at DoggoDotJl on Youtube. #notsponsored

Named Tuples

Named Tuples are just Tuples…but with names. I bet you didn’t see that coming.Well, okay… Named Tuples are a little more than just Tuples with names.Let’s look at the syntax and then talk a little more about it:

julia> tupleWithNames = (           language = "Julia",           isTheBest = true,           type = "NamedTuple",       )(language = "Julia", isTheBest = true, type = "NamedTuple")julia> typeof(tupleWithNames)@NamedTuple{language::String, isTheBest::Bool, type::String}

A NamedTuple functions like a JSON object in that you have key-value pairs. They are a fantastic data structure to use when you want to organize your code but don’t need the flexibility of an array. When you want to access the elements of a NamedTuple you can do it in two different ways.

julia> function testingTuples(ourTuple)            println("We can use $(ourTuple.type) to check if $(ourTuple.language) is better than Python")            println("Is $(ourTuple.language) the best? $(ourTuple[2] ? "Yes it is! .jl > .py" : "No, use Python noob")")endtestingTuples (generic function with 1 method)julia> testingTuples(tupleWithNames)We can use NamedTuple to check if Julia is better than PythonIs Julia the best? Yes it is! .jl > .py

You can see in the above example that Julia is indeed better than Python! Okay, maybe this example proves nothing about Julia’s superiority and may need to be the topic of another post. But this example does show how you can access elements inside NamedTuples. You can access elements in tuples using the tuple name, the . operator, and then the name of the element you want to access. You can also access it by indexing. Indexing is also how to access data in Tuples that weren’t special enough for you to give names to. Now, with access to Tuples and NamedTuples, you can organize data in the same way you would with JSON objects.

NOTE: Just like Tuples, NamedTuples are immutable. Once the values are set, they cannot be changed later.

Picture of a cool lizard

UnionAll Types

Now I know what you are probably thinking: What is up with the lizard?That can be explained with two simple points:

  • Lizards are cool.
  • I like to compare UnionAll types to chameleons.

Now, let me elaborate on that a bit. Just like chameleons change color, UnionAll types can change form and adapt to any data type. Chameleons don’t change their color to yellow and suddenly become bananas. They are still chameleons. Similarly, you may have a UnionAll type that is a float in one instance and an integer the next, but it still has the same structure. Enough with the lizard talk; let’s look at a more practical example to make sense of this reptilian elucidation.

julia> abstract type Shape{T} endjulia> struct Circle{T} <: Shape{T}           radius::T       endjulia> struct Square{T} <: Shape{T}           side::T       endjulia> circle_float = Circle(2.5)Circle{Float64}(2.5)julia> square_int = Square(4)Square{Int64}(4)julia> square_int isa Shapetruejulia> square_int isa Squaretruejulia> square_int isa Circlefalse

You can see that we created an abstract type called Shape, and then we created two other types called Square and Circle. By doing this, we have created a universal way to handle shapes without tying ourselves to any specific numeric type. We aren’t limited to just integers or just floats. UnionAll types, similar to other topics we covered, allow you to build very flexible code that is type-stable, generic, and optimal for machine code generation.

Type Aliases

The last thing we will cover is type aliases.Julia allows type aliases to give a new name to an existing type.The idea here is the same as many other concepts we discussed: generic and reusable code.Take Int, for example. When you specify a number as an Int, Julia will check if it needs to be an Int32 or Int64,depending on your system. The Int alias is a convenience operator, removing the abstraction of 32-bit or 64-bit values. You specify a variable as Int, and Julia takes care of the rest.We should probably note that Julia does not have Float as an alias for specific size floating point numbers (such as Float64).Int reflects the exact size of the pointer native to the machine you are running your code on, whilefloating points, on the other hand, are specified by the IEEE-754 standard. So, just like in real school, you have some homework to do later…or not. I won’t judge.

Summary

To recap, we have covered Type Unions, Parametric Types, Tuple Types, UnionAll Types, and Type Aliases.Hopefully, you have a better understanding of the Julia type system and the power it wields.If you want to learn more about Julia or some other interesting topics,be sure to check out our other blog posts on blog.glcs.io!

Additional Links

Background for article cover image by Freepik