# Wrapping Unitful macros

**URL:** <https://discourse.julialang.org/t/wrapping-unitful-macros/95171>\
**Category:** General Usage\
**Tags:** macros, unitful\
**Created:** [February 24, 2023, 11:39pm UTC](https://discourse.julialang.org/t/wrapping-unitful-macros/95171 "2023-02-24T23:39:15Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)\
**Post date:** [February 24, 2023, 11:39pm UTC](https://discourse.julialang.org/t/wrapping-unitful-macros/95171/1 "2023-02-24T23:39:15Z")

</div>

Inspired by a [commenter](https://github.com/PainterQubits/Unitful.jl/issues/306#issuecomment-892667919) on Unitful.jl I’m trying to do unitful calculations with bookshelves: how many books do I have in 10 book cases?

(20book / shelf) \* (8shelf / case) \* (10case) == 1600book

I would like to replace the complicated invocations of `@dimension` and `@refunit` with a simple `@countable book` as suggested in the issue, but I’m having trouble with the macro.

```julia
using Unitful

@dimension BookDimension "BookDimension" BookDim
@refunit book "book" Book BookDimension false

@dimension ShelfDimension "ShelfDimension" ShelfDim
@refunit shelf "shelf" Shelf ShelfDimension false

@dimension CaseDimension "CaseDimension" CaseDim
@refunit case "case" Case CaseDimension false

@assert (20book / shelf) * (8shelf / case) * (10case) == 1600book

macro countable(name)
    quote
        Unitful.@dimension $(name)Dimension "$(name)Dimension" $(name)Dim
        Unitful.@refunit $name "$name" $name $(name)Dimension false
    end
end

@countable book
@countable shelf
@countable case

```

```julia
@countable book
ERROR: LoadError: MethodError: no method matching var"@dimension"(::LineNumberNode, ::Module, ::Symbol, ::Symbol, ::Expr, ::Symbol, ::Symbol)

```

---

<div class="post-metadata">

**Author:** ![bertschi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bertschi/32/33462_2.png) [@bertschi](https://discourse.julialang.org/u/bertschi)\
**Post date:** [February 25, 2023, 9:39pm UTC](https://discourse.julialang.org/t/wrapping-unitful-macros/95171/2 "2023-02-25T21:39:55Z")

</div>

For good reasons, macros do not work by substituting strings, but instead represent code as a data structure, i.e., if you want to insert new names in the expansion you have to create them first:

```julia
macro countable(name)
    # name is the symbol :book when calling `@countable book`
    namestr = uppercasefirst(string(name)) # This is now the string "Book"
    dimsym = Symbol(namestr, "Dim") # From that, we create the symbol `:BookDim`
    dimstr = namestr * "Dimension" # and the string "BookDimension"
    # Using this new symbol and string, the desired expression can be constructed
    esc(quote
        Unitful.@dimension $(Symbol(namestr, "Dimension")) $dimstr $dimsym
        # Insert your second expression here ...
    end)
end

```

Note that `esc`aping the whole expansions is considered bad practice as it breaks macro hygiene. Here, it seems to be required as `@dimension` in turn introduces additional symbols for type parameters which are incorrectly namespaced otherwise.

```julia
julia> @macroexpand1 @countable book
quote
    #= REPL[2]:8 =#
    #= REPL[2]:8 =# Unitful.@dimension BookDimension "BookDimension" BookDim
end

julia> @countable book
BookDimension

```

---

<div class="post-metadata">

**Author:** ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)\
**Post date:** [March 18, 2023, 9:44pm UTC](https://discourse.julialang.org/t/wrapping-unitful-macros/95171/3 "2023-03-18T21:44:54Z")

</div>

Filling out the demo

```julia
using Unitful
macro countable(name)
    namestr = uppercasefirst(string(name))
    dimsym = Symbol(namestr, "Dim")
    dimstr = namestr * "Dimension"
    esc(quote
        Unitful.@dimension $(Symbol(namestr, "Dimension")) $dimstr $dimsym
        Unitful.@refunit $name $(string(name)) $(Symbol(namestr)) $(Symbol(dimstr)) false
    end)
end

@countable book
@countable shelf
@countable case

@assert (20book / shelf) * (8shelf / case) * (10case) == 1600book

```
