# How to automate a laborous task (via Metaprogramming? Need to access variables of the current working space)

**URL:** <https://discourse.julialang.org/t/how-to-automate-a-laborous-task-via-metaprogramming-need-to-access-variables-of-the-current-working-space/36438>\
**Category:** General Usage\
**Tags:** question, metaprogramming, design\
**Created:** [March 24, 2020, 12:09pm UTC](https://discourse.julialang.org/t/how-to-automate-a-laborous-task-via-metaprogramming-need-to-access-variables-of-the-current-working-space/36438 "2020-03-24T12:09:21Z")\
**Posts on this page:** 1\
**Showing post:** 3

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [March 24, 2020, 6:53pm UTC](https://discourse.julialang.org/t/how-to-automate-a-laborous-task-via-metaprogramming-need-to-access-variables-of-the-current-working-space/36438/3 "2020-03-24T18:53:18Z")

</div>

The first tool in your toolkit should always be functions. Macros are primarily used to introduce new syntax. A good rule of thumb for macro usage is provided in [this comment](https://discourse.julialang.org/t/how-to-warn-new-users-away-from-metaprogramming/35022/19) on a different thread: “you should know exactly what your macro is converting things to, otherwise the macro has gone too far.” In other words, macros are for transforming syntax, and functions are for computations. So normally when you are solving a new problem, you should first figure out how to solve it with functions (and perhaps user defined types). Then, _maybe_ you can consider introducing new syntax with a macro that uses your functions under the hood.

I don’t remember much about crystal structure, so let’s consider the following simplified problem. We start with a point in space (represented by a position vector) and we have two possible operations on it: _translation_ and _rotation_. Translation is represented by adding a vector to the position vector. Rotation is represented by multiplying the position vector with a rotation matrix. Let’s write a helper function that creates a rotation matrix for a given rotation angle in degrees:

```julia
function rotation(angle)
    [cosd(angle) -sind(angle);
     sind(angle) cosd(angle)]
end

```

Julia is already pretty smart about vector and matrix algebra, so if we try to add a rotation to a transformation, it won’t let us, because you can’t add a vector to a matrix:

```julia
julia> R = rotation(30)
2×2 Array{Float64,2}:
 0.866025 -0.5     
 0.5 0.866025

julia> T = [2, 3]
2-element Array{Int64,1}:
 2
 3

julia> R + T
ERROR: DimensionMismatch("dimensions must match")

```

Ok, so if we want to translate a point and then rotate it, how do we do it? Like this:

```julia
julia> v = [1, 1]
2-element Array{Int64,1}:
 1
 1

julia> R * (v + T)
2-element Array{Float64,1}:
 0.598076211353316
 4.964101615137754

```

Of course we can turn this into a function so that we can perform the same operation on any point:

```julia
julia> translate_then_rotate(v) = rotation(30) * (v + [2, 3])
translate_then_rotate (generic function with 1 method)

julia> translate_then_rotate([3, 5])
2-element Array{Float64,1}:
 0.33012701892219276
 9.428203230275509

```

If regular vectors and matrices are not enough to describe your crystal operators, and you need more complicated behavior from `*` and `+`, then you can create your own [user-defined types](https://docs.julialang.org/en/v1/manual/types/) and add new [methods](https://docs.julialang.org/en/v1/manual/methods/) to `*` and `+` so that the arithmetic operators have special behavior for your custom types.

For example, if you wanted to print a more informative error message when you try to add a rotation to a transformation, you could use something like the following.

```julia
struct Rotation
    angle::Float64
    matrix::Matrix{Float64}

    # Define an inner constructor. This guarantees that
    # the matrix field contains a valid rotation matrix.
    function Rotation(angle)
        matrix = [cosd(angle) -sind(angle);
                  sind(angle) cosd(angle)]
        new(angle, matrix)
    end
end

struct Translation
    t::Vector{Float64}
end

function Base.:+(::Rotation, ::Translation)
    throw(ArgumentError("Rotations and translations cannot be added."))
end

Base.:+(t::Translation, r::Rotation) = r + t

```

Let’s try it out in the REPL:

```julia
julia> R = Rotation(30)
Rotation(30.0, [0.8660254037844386 -0.5; 0.5 0.8660254037844386])

julia> T = Translation([2, 3])
Translation([2.0, 3.0])

julia> R + T
ERROR: ArgumentError: Rotations and translations cannot be added.

julia> T + R
ERROR: ArgumentError: Rotations and translations cannot be added.

```

Hopefully that gives you some ideas to get started.

---

_[View the full topic](https://discourse.julialang.org/t/how-to-automate-a-laborous-task-via-metaprogramming-need-to-access-variables-of-the-current-working-space/36438)._
