How To Create Exceptions In Julia
How to create and raise exceptions in Julia

Lately, I have been struggling to find topics to discuss on my blog because I am currently invested in developing my web-framework and things right now. However, while working on Toolips, I did implement some exceptions via Base and I realized that this would make a really cool topic from an article. Today we are going to be extending Julia’s Base by designing our own exception and binding it to a few simple methods.
Abstract Type Exception
In order to properly inherit methods, in most cases, we need to pay attention to its position on the type hierarchy. There are common consistencies amongst exceptions in general such as a common field msg and code that will be automatically used to bind your exception to more Base methods. Although the example will not be including this, it might be a great idea, seeing as there is likely a-lot more dispatch to be inherited correctly. As such, we start this process by making our new Exception type, which needs to be a sub-type of Exception — an abstract type from Base. I also like to directly import any type I use abstractly, I just imagine this could help with precompilation to some degree; directly importing the types. I have not tested this concept, it just seems realistic that Julia typing might have an easier time adding methods to a type that is directly imported. Alongside this import, we are also going to want to import Base.showerror , as this is how we will be designing how we want to raise our exceptions.
import Base: showerror, Exceptionmutable struct MyException <: Exception
type::Type
except::Exception
endIn this basic example, we are meant to catch when an error happens, and then record both the type and the exception that caused this error.
base.showerror
The next step in this process is binding the showerror method from Base, which of course gets called whenever stdout shows an error.
showerror(io::IO, e::MyException) = print(io, "MyException !: by $e")With this little bit of code, we are already off to the races. Finally, we will build a little example that will catch our error if we cannot add it to an Int64. this will produce a MethodError for no method matching +(::String ::Int64) if we passed a String, for example, but happily will take an Integer, which is why it is probably a great idea to keep your dispatch annotations not set to Any, but anyway …
julia> function teste(x::Any)
try
x += 1
catch e
throw(MyException(typeof(e), e))
end
return x
endI decided to use +=, which is slightly different, but also gives a return — which is nice. Testing an integer works fine:
julia> teste(5)
6However, when the input is a string:
julia> teste("hello")
ERROR: MyException !: by MyException(MethodError, MethodError(+, ("hello", 1), 0x0000000000007a6b))
Stacktrace:
[1] teste(x::String)
@ Main ./REPL[4]:5
[2] top-level scope
@ REPL[6]:1Whoops, I accidentally provided myexception instead of raising the method error. We were actually looking for the fieldname except on e:
showerror(io::IO, e::MyException) = print(io, "MyException !: by $(e.except)")Implementing this,
julia> showerror(io::IO, e::MyException) = print(io, "MyException !: by $(e.except)")
showerror (generic function with 56 methods)julia> teste("hello")
ERROR: MyException !: by MethodError(+, ("hello", 1), 0x0000000000007a6c)
Stacktrace:
[1] teste(x::String)
@ Main ./REPL[4]:5
[2] top-level scope
@ REPL[8]:1caused by: MethodError: no method matching +(::String, ::Int64)
Closest candidates are:
+(::Any, ::Any, ::Any, ::Any...) at /opt/julia-1.6.3/julia-1.6.3/share/julia/base/operators.jl:655
+(::T, ::T) where T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8} at /opt/julia-1.6.3/julia-1.6.3/share/julia/base/int.jl:87
+(::Base.TwicePrecision, ::Number) at /opt/julia-1.6.3/julia-1.6.3/share/julia/base/twiceprecision.jl:279
...
Stacktrace:
[1] teste(x::String)
@ Main ./REPL[4]:3
[2] top-level scope
@ REPL[8]:1In Julia, Base extensibility, and being able to implement your own tools in an instant, culminates into a really awesome programming experience. The difference in most languages is that they are not built from the beginning, at the core of their paradigm, to work in the same way Julia does. With Julia, multiple methods per type and adding methods together from different places was a focus. Even in languages that might let you do similar things with their built-ins, it is often much more cobbled together — and extending it is not at all as fluid as it is seen here.
A great example of this would be Python. In Python, in order to add indexing we have to add some weird default PyObject function into a class, in Julia we just use multiple dispatch and bind the getindex() method to our type. Not only is it easier, but it also makes for a lot less code to glue things like that together. Nowhere else could you create a lot of the things in such a short amount of time. Thank you for reading, and if you are considering looking into Julia, I cannot recommend it enough!





