Again and again I see syntax with @named
, for example
@named source = Voltage()
What does it mean? What @ mean in jullia?
Thanks! Sorry if question is obvious, but after 30 mins of googling I still cant figure out ;(
Again and again I see syntax with @named
, for example
@named source = Voltage()
What does it mean? What @ mean in jullia?
Thanks! Sorry if question is obvious, but after 30 mins of googling I still cant figure out ;(
The @-symbol indicates a macro, whereas function names start with a regular letter.
Thanks! So what does @named
do? Which package defines it?
What packages are loaded with using
or import
in the examples/code you’ve been looking at?
Oh, I understand now) I thought it is from standard library
In all likelyhood, that macro comes from ModellingToolkit.jl, in which case it means replace this
@named source = Voltage()
With this
source = Voltage(name=:source)
It passes the variable name as an argument to the function to avoid you having to write it twice.
I do wonder about the net benefit of these macros, given the confusion they generate for saving 7 characters.
I don’t know, it also stops you from (accidentally) naming your variable differently in the two parallel systems. It’s like
x = 123
println("2 * $x = $(2*x)")
over
println("2 * 123 = $(2*122)")
is not just a waste of characters, retyping is a source of oopsies.
It does require you to read the docs, and macros are inherently a bit more obscure, but I’m a big fan of only typing things once.
I’m sure it has benefits.
But someone new just put over half an hour into trying to read it and bothered to comment here, meaning probably ten other people have done that and not commented.
I say this because after using similar macros everywhere for a few years I went through a lot of code and removed them. Onboarding new people was just too confusing.
You are right! Thanks a lot!
The @macroexpand
macro allows you to see how a macro transforms your code.
julia> @macroexpand @named source = Voltage()
:(source = begin
#= ~\.julia\packages\ModelingToolkit\gZ57L\src\systems\abstractsystem.jl:886 =#
var"#####__is_system_construction####313" = Voltage isa DataType && Voltage <: ModelingToolkit.AbstractSystem
#= ~\.julia\packages\ModelingToolkit\gZ57L\src\systems\abstractsystem.jl:887 =#
Voltage(; name = :source)
end)
In my mind, there are a three different classes of macros.
@async
, @sync
)Because in the case of @named
you could easily write similar code, this looks situation #1 above. In this case you probably should avoid using a macro. This is mainly for personal convenience.
For the second class of macro, @async
hides an implementation detail, the sticky attribute of Task
, for example and makes launching asynchronous tasks very easy, so perhaps this is a useful facility. While one could technically do what @async
does by hand or via a functional interface, it would be difficult to remember or implement.
In the third scenario, one example of a macro that lets you do something that you would have a hard macro are the inline string macros I recently added to InlineStrings.jl
julia> f() = inline15"hello world"
f (generic function with 1 method)
julia> @code_llvm f()
; @ REPL[52]:1 within `f`
; Function Attrs: uwtable
define void @julia_f_3562(i128* noalias nocapture sret(i128) %0) #0 {
top:
store i128 138766332635614100027273860204845858827, i128* %0, align 8
ret void
}
julia> g() = InlineString15("hello world")
g (generic function with 1 method)
julia> @code_llvm g()
; @ REPL[54]:1 within `g`
; Function Attrs: uwtable
define void @julia_g_3564(i128* noalias nocapture sret(i128) %0) #0 {
top:
%1 = alloca i128, align 8
call void @j_String15_3566(i128* noalias nocapture nonnull sret(i128) %1, {}* inttoptr (i64 2556968488496 to {}*)) #0
%2 = load i128, i128* %1, align 8
store i128 %2, i128* %0, align 8
ret void
}
In this case using a macro allows you to generate code that would be very difficult if not impossible to write by hand, encoding a static string as an integer. This is the best use of macros in my view.
Exactly! Macros are best used when long or technically difficult code is hidden behind an abstraction that is much easier to understand than the details it replaces - like @time
and @async
.
Overall they improve comprehension of code by showing the intent rather than the detail.
@named
seems harder to understand than the code it replaces.