Way to treat function as symbol using boolean flag?

question

#1

Let’s say you have a bunch of coefficients, i.e.

function K_PC(cur_blah::AbstractBlah)
  cur_K = K_law(cur_blah)

  cur_K *= K_G(cur_blah) ^ 2
  cur_K *= 1.273

  cur_K /= cur_blah.eta_T

  cur_K
end

However, you’re using SymPy and want to enable some sort of symbolic mode

// that doesn’t evaluate any of the values (i.e. leaves the output pretty)

Is there a way to know the name of K_PC inside itself?


This would allow you to short-circuit the function at the top with:

return SymPy.symbols(<name_of_function>)

// where <name_of_function> would be K_PC in this example


#2

You could write a macro

@name function K_PC(cur_blah::AbstractBlah)
  ...
end

That expands to something like

function K_PC(cur_blah::AbstractBlah)
    __FUNCTION_NAME__ = :K_PC
  ...
end

#3

I think string(stacktrace()[1].func) will return the function’s own name (and [2] the function calling it, etc).


#4

An alternative solution if you merely need it for DRY and want to avoid macros is to define your operations as types, then make them callable:

abstract type AbstractOp end

struct Op1 <: AbstractOp end

function (op::Op1)(arg)
    Symbol(typeof(op))
end

Also, you can define a

sympyname(::Op1) = :op1

and thus decouple the two namespaces.


#5

Pretty cool solutions across the board! Thanks for your help.


The code I ended up on is:

macro symbol_func(cur_expr::Expr)
  @assert cur_expr.head == :function

  cur_call = cur_expr.args[1]
  cur_func_name = cur_call.args[1]
  cur_main_var = cur_call.args[2].args[1]

  cur_param = Expr(:kw, Expr(:(::), :is_direct_call, :Bool), false)

  cur_sub_expr = parse("""
    $(cur_main_var).is_symbolic && !is_direct_call &&
      return symbols("$(cur_func_name)")
  """)

  push!(cur_expr.args[1].args, cur_param)
  unshift!(cur_expr.args[2].args, cur_sub_expr)

  cur_expr
end

With the initial code now being:

@symbol_func function K_PC(cur_blah::AbstractBlah)
  cur_K = K_law(cur_blah)

  cur_K *= K_G(cur_blah) ^ 2
  cur_K *= 1.273

  cur_K /= cur_blah.eta_T

  cur_K
end

#6

I was using this approach for a precondition marco that wants to use the function name in an error message. It turned out not to be generally reliable. Possibly because issue #19979 means that stack traces sometimes get corrupted when try/catch is used? Possibly due to inlining?

(Another variation of a precondition macro that needs the function name: https://github.com/JuliaLang/julia/pull/15495)


#7

@srp asked for @__FUNCTION__ a while back.

This issue should be re-opened because none of the referenced issues have implemented anything like @__FUNCTION__


#8

Forgot to post a picture of the use case!

(left is a paper. right is code. and they match!)


update: already found 2 bugs in some code using this :grimacing:


#9

I’d also love to see @__FUNCTION__ added.