# How to write a simple macro

**URL:** <https://discourse.julialang.org/t/how-to-write-a-simple-macro/83420>\
**Category:** General Usage\
**Tags:** question, macros\
**Created:** [June 27, 2022, 6:28pm UTC](https://discourse.julialang.org/t/how-to-write-a-simple-macro/83420 "2022-06-27T18:28:26Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [June 27, 2022, 6:28pm UTC](https://discourse.julialang.org/t/how-to-write-a-simple-macro/83420/1 "2022-06-27T18:28:26Z")

</div>

I have lines similar to this a lot in my code:

```julia
if chi_factor > 0.85
    chi_factor = 0.85
end

```

I would like to write it like this:

```julia
@limit chi_factor 0.85

```

I cannot use a function for this because a function cannot change the value of a scalar argument.

Any idea how to write such a macro?

---

<div class="post-metadata">

**Author:** ![StevenWhitaker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevenwhitaker/32/9749_2.png) [@StevenWhitaker](https://discourse.julialang.org/u/StevenWhitaker)\
**Post date:** [June 27, 2022, 6:35pm UTC](https://discourse.julialang.org/t/how-to-write-a-simple-macro/83420/2 "2022-06-27T18:35:04Z")

</div>

Something like this should work:

```julia
macro limit(name::Symbol, value)
    return esc(quote
        if $name > $value
            $name = $value
        end
    end)
end

```

```julia
julia> x = 1; @limit(x, 0.4); x
0.4

julia> x = 1; @limit(x, 1.4); x
1

```

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [June 27, 2022, 6:38pm UTC](https://discourse.julialang.org/t/how-to-write-a-simple-macro/83420/3 "2022-06-27T18:38:33Z")

</div>

why not just `chi_factor = min(chi_factor, .85)`?

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [June 27, 2022, 6:41pm UTC](https://discourse.julialang.org/t/how-to-write-a-simple-macro/83420/4 "2022-06-27T18:41:40Z")

</div>

Because then you need to write the name of the variable twice and my colleagues who love C will be laughing at me if I tell them thats the way to do it in Julia…

And I also sometimes need a macro that limits in a range, and then it would be

```julia
chi_factor = max(min(chi_factor, 0.85), 0.4)

```

which is hard to read. Easier to read:

```julia
@limit chi_factor 0.4 0.85

```

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [June 27, 2022, 6:46pm UTC](https://discourse.julialang.org/t/how-to-write-a-simple-macro/83420/5 "2022-06-27T18:46:46Z")

</div>

This works for me:

```julia
macro limit(name::Symbol, value)
    return esc(quote
        if $name > $value
            $name = $value
        else
            $name
        end
    end)
end

height = 12.0
@limit height 10.0
println(height)

height = 5.0
@limit height 10.0
println(height)

```

Thanks for giving me a starting point for writing macros! 🙂

---

<div class="post-metadata">

**Author:** ![devel-chm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/devel-chm/32/3572_2.png) [@devel-chm](https://discourse.julialang.org/u/devel-chm)\
**Post date:** [June 27, 2022, 6:57pm UTC](https://discourse.julialang.org/t/how-to-write-a-simple-macro/83420/6 "2022-06-27T18:57:42Z")

</div>

This works:

```julia
clamp!([x],0.85,Inf)

```

I’m sure a function could be used as well as an `@macro` here too.

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [June 27, 2022, 7:02pm UTC](https://discourse.julialang.org/t/how-to-write-a-simple-macro/83420/7 "2022-06-27T19:02:48Z")

</div>

Sorry, this does NOT work:

```julia
height = 12.0
# println(@limit height 10.0)
clamp!([height], 5.0, 10.0)
println(height)

height = 4.0
# println(@limit height 10.0)
clamp!([height], 5.0, 10.0)
println(height)

```

It does NOT change the variable height.

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [June 27, 2022, 7:09pm UTC](https://discourse.julialang.org/t/how-to-write-a-simple-macro/83420/8 "2022-06-27T19:09:21Z")

</div>

Bonus question:

How can I write a macro `@limit` that excepts either two or three parameters, such that

```julia
@limit height 10.0

```

and

```julia
@limit height 5.0 10.0

```

both work?

---

<div class="post-metadata">

**Author:** ![devel-chm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/devel-chm/32/3572_2.png) [@devel-chm](https://discourse.julialang.org/u/devel-chm)\
**Post date:** [June 27, 2022, 8:28pm UTC](https://discourse.julialang.org/t/how-to-write-a-simple-macro/83420/9 "2022-06-27T20:28:44Z")

</div>

It looks like I got caught by the `[]` making a copy.  
You are correct, it only worked from the top level of the global-ish REPL.

---

<div class="post-metadata">

**Author:** ![dylanxyz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dylanxyz/32/36646_2.png) [@dylanxyz](https://discourse.julialang.org/u/dylanxyz)\
**Post date:** [June 27, 2022, 8:44pm UTC](https://discourse.julialang.org/t/how-to-write-a-simple-macro/83420/10 "2022-06-27T20:44:07Z")

</div>

I think this should work:

```julia
macro limit(name::Symbol, min, max=min)
    return esc(quote
        $name = clamp($name, $min, $max)
    end)
end

```

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [June 27, 2022, 9:15pm UTC](https://discourse.julialang.org/t/how-to-write-a-simple-macro/83420/11 "2022-06-27T21:15:53Z")

</div>

> [@ufechner7](#):
>
> you need to write the name of the variable twice and my colleagues who love C will be laughing at me if I tell them thats the way to do it in Julia

Incidentally, what is the C way to write `chi_factor = clamp(chi_factor, low, high)` without writing the variable twice, once to access it and once to reassign it?

PS this has been implied already, but `clamp` works on scalars while `clamp!` works on arrays.

---

<div class="post-metadata">

**Author:** ![Jeff\_Emanuel](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeff_emanuel/32/15440_2.png) [@Jeff\_Emanuel](https://discourse.julialang.org/u/Jeff_Emanuel)\
**Post date:** [June 28, 2022, 12:22am UTC](https://discourse.julialang.org/t/how-to-write-a-simple-macro/83420/12 "2022-06-28T00:22:38Z")

</div>

> [@Benny](#):
>
> what is the C way to write…

With a macro,

```C
#define LIMIT(X,LOW,HIGH) ((X) = ((X)<(LOW)? (LOW) : ((X)>(HIGH) ? (HIGH) : (X)))

LIMIT(chi_factor, low, high);

```

---

<div class="post-metadata">

**Author:** ![hendri54](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hendri54/32/9621_2.png) [@hendri54](https://discourse.julialang.org/u/hendri54)\
**Post date:** [June 28, 2022, 11:51am UTC](https://discourse.julialang.org/t/how-to-write-a-simple-macro/83420/13 "2022-06-28T11:51:28Z")

</div>

Isn’t the intent to ignore the max if not provided? As in

```julia
macro limit(name::Symbol, vmin, vmax=typemax(vmin))
     return esc(quote
          $name = clamp($name, $vmin, $vmax)
     end)
end

```

It would probably be better to use `typemax(name)` as the default for `vmax`, but I don’t know how to implement this.

---

<div class="post-metadata">

**Author:** ![PeterSimon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/petersimon/32/25193_2.png) [@PeterSimon](https://discourse.julialang.org/u/PeterSimon)\
**Post date:** [June 28, 2022, 2:12pm UTC](https://discourse.julialang.org/t/how-to-write-a-simple-macro/83420/14 "2022-06-28T14:12:37Z")

</div>

> [@hendri54](#):
>
> better to use `typemax(name)` as the default for `vm`

You can’t do this with an ordinary macro because types aren’t known at macro expansion time. However, you could just use two methods:

```julia
macro limit(name::Symbol, vmin, vmax)
     return esc(quote
          $name = clamp($name, $vmin, $vmax)
     end)
end

macro limit(name::Symbol, vmin)
     return esc(quote
          $name = min($name, $vmin)
     end)
end

```

Edit: fixed 2-argument version to replace `max` by `min`. I misunderstood the OP’s intent for the 2-argument version.

---

<div class="post-metadata">

**Author:** ![dylanxyz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dylanxyz/32/36646_2.png) [@dylanxyz](https://discourse.julialang.org/u/dylanxyz)\
**Post date:** [June 28, 2022, 6:43pm UTC](https://discourse.julialang.org/t/how-to-write-a-simple-macro/83420/15 "2022-06-28T18:43:18Z")

</div>

With your version, there’s a problem when using with only 2 arguments:

```julia
julia> x = 4
4

julia> @limit x 2
4

julia> x # here, x should be 2, because we are limiting it to a max value of 2
4

julia> y = 8
8

julia> @limit y 9
9

julia> y # y should still be 8 here
9

```

Also, if the user chooses to pass a variable as argument instead of a number:

```julia
julia> max = 10
10

julia> @limit y max
ERROR: LoadError: MethodError: no method matching typemax(::Symbol)
Closest candidates are:
  typemax(::Union{Dates.DateTime, Type{Dates.DateTime}}) at C:\Users\jorge\AppData\Local\Programs\Julia-1.7.2\share\julia\stdlib\v1.7\Dates\src\types.jl:453
  typemax(::Union{Dates.Date, Type{Dates.Date}}) at C:\Users\jorge\AppData\Local\Programs\Julia-1.7.2\share\julia\stdlib\v1.7\Dates\src\types.jl:455
  typemax(::Union{Dates.Time, Type{Dates.Time}}) at C:\Users\jorge\AppData\Local\Programs\Julia-1.7.2\share\julia\stdlib\v1.7\Dates\src\types.jl:457
  ...
Stacktrace:
 [1] var"@limit"( __source__ ::LineNumberNode, __module__ ::Module, name::Symbol, vmin::Any)
   @ Main .\REPL[1]:2
in expression starting at REPL[11]:1

```

And yes, my version of the macro is still wrong, (it only works properly with 3 arguments):

```julia
julia> x = 4
4

julia> @limit x 2
2

julia> x # should be 2, so here it works
2

julia> y = 8
8

julia> @limit y 9
9

julia> y # should be 8 here
9

```

But this version works with both 2 and 3 arguments:

```julia
macro limit(name, min, max=nothing)
    if isnothing(max)
        max = min
        min = :(typemin($min))
    end

    return esc( :($name = clamp($name, $min, $max)) )
end

```

```julia
julia> x = 4
4

julia> @limit x 2
2

julia> x
2

julia> y = 8
8

julia> @limit y 9
8

julia> y
8

julia> z = 10
10

julia> @limit z 5 8
8

julia> z
8

julia> w = 5
5

julia> min, max = 2, 10
(2, 10)

julia> @limit w min max
5

julia> w
5

```

---

<div class="post-metadata">

**Author:** ![ctkelley](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ctkelley/32/10684_2.png) [@ctkelley](https://discourse.julialang.org/u/ctkelley)\
**Post date:** [June 28, 2022, 7:01pm UTC](https://discourse.julialang.org/t/how-to-write-a-simple-macro/83420/16 "2022-06-28T19:01:09Z")

</div>

> [@ufechner7](#):
>
> I cannot use a function for this because a function cannot change the value of a scalar argument.

Could you make chi\_factor and array with one entry ie

```julia
chi_factor=zeros(1)

```

and then you can update that entry as you would any array.

```julia
function limit!(chi_factor,clim)
    if chi_factor[1] > clim
       chi_factor[1]=clim
   end
end

```

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [June 28, 2022, 7:26pm UTC](https://discourse.julialang.org/t/how-to-write-a-simple-macro/83420/17 "2022-06-28T19:26:11Z")

</div>

> Could you make chi\_factor and array with one entry ie

I know, but that is not a good, practical solution. I have lots of values I want to limit, they are all scalars and are passed around from other functions and modules…

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [June 28, 2022, 7:50pm UTC](https://discourse.julialang.org/t/how-to-write-a-simple-macro/83420/18 "2022-06-28T19:50:19Z")

</div>

The practical solution would probably be to create a mutable struct, or a Dict, with all the values and restrict on them. But a macro is probably a solid second place.

---

<div class="post-metadata">

**Author:** ![hendri54](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hendri54/32/9621_2.png) [@hendri54](https://discourse.julialang.org/u/hendri54)\
**Post date:** [June 29, 2022, 12:54pm UTC](https://discourse.julialang.org/t/how-to-write-a-simple-macro/83420/19 "2022-06-29T12:54:18Z")

</div>

I misunderstood what the 2 argument version of `@limit` was mean to do. Thanks for correcting.

---

<div class="post-metadata">

**Author:** ![tomlee](https://avatars.discourse-cdn.com/v4/letter/t/f08c70/32.png) [@tomlee](https://discourse.julialang.org/u/tomlee)\
**Post date:** [June 29, 2022, 10:31pm UTC](https://discourse.julialang.org/t/how-to-write-a-simple-macro/83420/20 "2022-06-29T22:31:21Z")

</div>

I think it is worth pointing out that when using macros you should take care not to make the code more challenging to read. `x = clamp(x, lo, hi)` takes much less effort to understand than `@limit x lo hi`, particularly for a newcomer to the code base, because you don’t then have to go to the macro and figure out what it means. The macro is also then another piece of code you have to write, test and maintain. I have certainly encountered cases where I am trying to debug or modify someone else’s Julia package and have struggled to figure out what a poorly-documented macro is actually doing.

In this case, the macro is relatively simple, and if this operation is done all over the place it might be worth it. But it is always worth thinking carefully about how macros impact readability. Reducing number of characters typed is not usually the best optimization strategy. Nor is avoidance of teasing from unenlightened C programmers 😉
