5 Of My Favorite Julia Macros | by Emmett Boudreau | Towards Data Science

The Julia Programming language is one that has brought an entirely new feature-set to the world of Data Science. And the convenience of a lot of the features have been re-realized, especially in Data Science applications, at least this is certainly the case for me. One feature from the world of Lisp and meta-programming I have really enjoyed in the Julia programming language is the addition of macros. Not only are these convenient for meta-programming, but they are also pretty great to be used in a more general sense. They can serve as a dispatched shortcut to the utilization of certain functions, and can make values into expressions automatically without a call. Since I like the macros in Julia so much, I wanted to show some of my favorite macros that you might want to try in your own Julia programming!
Notebook
The code_llvm macro is awesome, in my opinion. It can be used to view the actual llvm code that Julia uses on the back-end. This is incredibly useful for actually analyzing the performance of Julia code from a lower-level perspective. Furthermore, I think it is a really good way to learn more about the Julia compiler and how it works. The macro takes a method or expression, and returns the llvm code that is used to get the return. Consider the following function, for example:
function ThisIsAFunction(n)    n = n + 5    n - 1    for z in 1:n        n -= 1    end    return(n)end
This function will take any value, and then take an incremental approach to subtracting it to zero by one. This is after adding five, then subtracting one, so it is a pretty silly function, but it will demonstrate how looping and operations look when it comes to the code_llvm macro:
@code_llvm ThisIsAFunction(20)
The next macro I absolutely love is the assert macro. This macro is used to provide a quick throw when a condition is not met. This is mostly used for testing. I am not sure exactly where I heard this, maybe it was in school, but you should not typically use assert for throws in code. I must confess, I am not sure of this statement’s validity, or where I originally heard it — but I suspect I heard it from my professor. Anyway, we can use this macro by firstly providing a condition that needs to be met, and then providing an error statement. Perhaps the reason that we should not use this in real package development, but instead testing, is because the error return for this will always be the same, with only the string text identifying what is wrong. When using this macro, the throw will always be an AssertionError. Consider the following arrays:
y = [5, 10, 15]x = [5, 10]
Let us say that we wanted these to be of the same length:
@assert length(x) == length(y) "X and Y dimensions do not match!"
Another really cool macro in Julia’s Base is the macroexpand macro. This macro can be used to view a macro as a set of symbols. Symbols in a lot of ways are the back-bone of Julian meta-programming, and this is an easy way to take things to the next level by making macros into symbols. This can give a broader understanding of macros, as at first glance one might see macros as function equivalents, but this is just not the case. With this macro, it is easy to see why macros are so prevalent in meta-programming in Julia.
@macroexpand @assert length(x) == length(y) "X and Y dimensions do not match!"
If we were to assert this to a new variable alias, and then check the type, we would see that this returns an expression:
z = @macroexpand @assert length(x) == length(y) "X and Y dimensions do not match!"typeof(z)
This means that we can use this expression with other meta-programming methods, such as eval:
eval(z)
Testing in Julia is done with a completely different methodology than what is in many other programming languages. If you ask me, I subjectively love their implementation, as I think it is a great way to keep tests organized. If you would like to read more on how to test Julia packages, I actually wrote a whole article on just that you can check out here:
towardsdatascience.com
Testing in Julia utilizes macros to make testing a lot quicker and more convenient. This also makes it a lot easier on the testing packages end, because your test is given to Julia is an expression, meaning it can analyze and run it in any way it wants without the need for ever parsing your code. In Julia, we can start a test-set by using the @testset macro and providing a name:
@testset "Numbers" beginend
Then we can test some conditions by using the test macro, which is similar to assert, but instead returns its boolean to the testset macro.
using Test@testset "Numbers" begin   x = 5    y = 5    @test x == yend
When we run this, we get some pretty cool, well-organized, and comprehensive output of how our tests ran. It is very convenient to have this all automatically be generated into a table and organized with the titles of our sets, with no printing necessary. Have a look:
Another great macro in Julia’s base is the evalpoly macro. This macro evaluates a polynomial ∑k​zk−1c[k] for the coefficients c[1], c[2]. In other words, the coefficients are provided in ascending order by the power of z. The code that is used to calculate these numbers is very efficient, and I would certainly say that it is a great tool for scientific computing!
@evalpoly(3, 1, 0, 1, 5, 1, 2)
In-line math is inherently more efficient. In Julia, this is pretty easy to control using the inline and noline macros. These certainly come in handy if you are wishing to perform a lot of linear algebra, as applications of these macros can seriously slow down your consumption of memory. In general, working with most arrays inline is probably a good practice, especially if you want to save a lot of memory with your project:
@inline foo_in(n) = (n, Vector{Int}(n))@noinline foo_ni(n) = (n, Vector{Int}(n))  function ft_in(n)        s= 0        for i= 1:n         jj,v = foo_in(i)        s+=sum(v)        end        s end  function ft_ni(n)        s= 0        for i= 1:n         jj,v = foo_ni(i)        s+=sum(v)        end        s end  @time ft_in(1000)   0.001948 seconds (1.00 k allocations: 3.962 MiB)  @time ft_ni(1000)   0.002083 seconds (2.00 k allocations: 3.992 MiB)
Macros are an incredibly useful feature in the Julia programming language. Not only can they be used as a “ shortcut” to calling methods, but they can also be used to turn regular code into expressions automatically. Needless to say, this comes in handy in many different applications, such as the testing application. I hope that this article was interesting, and perhaps got you familiar with some new macros you might not have known about! Thank you very much for reading!