What @named mean

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 ;(

1 Like

The @-symbol indicates a macro, whereas function names start with a regular letter.

https://docs.julialang.org/en/v1/manual/metaprogramming/

1 Like

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?

1 Like

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.

3 Likes

I do wonder about the net benefit of these macros, given the confusion they generate for saving 7 characters.

3 Likes

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.

3 Likes

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.

4 Likes

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.

  1. The macro is pure syntactic sugar (this case). Don’t do this in core you plan to share.
  2. The macro provides an API while keeping the interface abstract (e.g. @async, @sync)
  3. The macro results in code that cannot be generated otherwise.

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.

3 Likes

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.

4 Likes