Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to execute some code the first time a Julia function is called?

Tags:

julia

I have a specific use case where I want a function to basically provide a warning the first time it is called to tell the user some info. Other than using a global counter and keeping track of the number of times the function is called, I am not sure how I can check this. Any ideas on specific Julia syntax that would allow me to check if the function is being called for the first time?

like image 832
logankilpatrick Avatar asked Mar 11 '26 22:03

logankilpatrick


1 Answers

Here are some ideas for how to do this when you have the option of rewriting the function body. All of these could also be realized by writing a rather simple macro performing the respective transformation. OK, it's not so trivial if you want to get top-level and local definitions working right.

(Non-)option 1

Conceptually, you could do this with a generated function, and it will work mostly when you try it out:

julia> @generated function dostuff(x)
           @warn "You really shouldn't do stuff!"
           return :(2x + 1)
       end
dostuff (generic function with 1 method)

julia> dostuff(1)
┌ Warning: You really shouldn't do stuff!
└ @ Main REPL[1]:2
3

julia> dostuff(1)
3

But: don't. The compiler is free to choose when to call the "generator", and to quote the docs: it is undefined exactly when, how often or how many times these side-effects will occur. Not a good idea.

Additionally, it is questionable whether @warn will use a printing function that is allowed within a generated function. In earlier Julias, using println instead of Core.println sometimes errored in generated functions, because the former modified the event loop.

Option 2

So for something better. Instead of your idea with a global counter, you can do something similar by defining the function as a closure of a let-bound variable:

julia> let isfirstcall = Threads.Atomic{Bool}(true)
           global function dostuff(x) 
               if Threads.atomic_xchg!(isfirstcall, false)
                   @warn "You really shouldn't do stuff!"
               end
               return 2x + 1
           end
       end
dostuff (generic function with 1 method)

julia> dostuff(1)
┌ Warning: You really shouldn't do stuff!
└ @ Main REPL[16]:4
3

julia> dostuff(1)
3

julia> isfirstcall
ERROR: UndefVarError: isfirstcall not defined

I have here chosen to use atomics just for the fun of atomic_xchg!, but if threading is not an issue, a plain boolean will be fine, too.

Option 3

Also, while avoidable, a global variable isn't too bad if you do it right. Which means: make it a const Ref. And (optionally, but recommended in this case), use a var string to give it a name not usually accessible to the user:

julia> const var"##isfirstcall" = Ref(true)

julia> function dostuff(x)
           if var"##isfirstcall"[]
               @warn "You really shouldn't do stuff!"
               var"##isfirstcall"[] = false
           end
           return 2x + 1
       end
dostuff (generic function with 1 method)

julia> dostuff(1)
┌ Warning: You really shouldn't do stuff!
└ @ Main REPL[22]:3
3

julia> dostuff(1)
3
like image 151
phipsgabler Avatar answered Mar 14 '26 12:03

phipsgabler



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!