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.
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
@countable book
ERROR: LoadError: MethodError: no method matching var"@dimension"(::LineNumberNode, ::Module, ::Symbol, ::Symbol, ::Expr, ::Symbol, ::Symbol)
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:
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 escaping 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> @macroexpand1 @countable book
quote
#= REPL[2]:8 =#
#= REPL[2]:8 =# Unitful.@dimension BookDimension "BookDimension" BookDim
end
julia> @countable book
BookDimension