Макрос для шаблона функции: устранение проблем

#metaprogramming #julia

#метапрограммирование #джулия

Вопрос:

Я пытаюсь определить макрос для шаблона функции, то есть шаблон, в который вы можете легко вставить соответствующий код, но не хотите раскрывать пользователю все подробные детали реализации. Например, я хотел бы что-то вроде этого, которое переносит выражение в более сложную функцию:

 macro make_complex_function(ex)
  quote
    function (alg,f,t,u,k)
      # Add some stuff on top
      condition1 = false
      condition2 = false
      #...
      #Put in the user's code
      $(esc(ex))
      # Put a footer
      return some,stuff,here,long,annoying,list
    end
  end
end
 

таким образом, пользователь может легко вставлять небольшие фрагменты логики (с помощью упрощенного API / документации):

 easy_func = @make_complex_function begin
   if u > 1
      print("oh no! It happened!")
    end
 end
 

в то время как полная мощность по-прежнему доступна для более продвинутых пользователей. Однако, если вы запустите этот код, вы получите доступ к неопределенным ссылочным ошибкам. Я думаю, это потому, что я неправильно экранирую выражение и должен каким-то образом экранировать всю функцию, но не уверен, как.

Ответ №1:

краткий ответ: не используйте esc и не размещайте его во всем quote блоке.

 julia> macro m(e)
         quote
           function f(x)
             return $(e)
           end
         end
       end
@m (macro with 1 method)

julia> f = @m x 1
#1#f (generic function with 1 method)

julia> f(2)
3

julia> macro m(e)
         esc(quote
           function f(x)
             return $(e)
           end
         end)
       end
@m (macro with 1 method)

julia> f = @m x 1
f (generic function with 1 method)

julia> f(2)
3
 

длинный ответ:

  1. кое-что о гигиене

julia добавит префикс к любым именам, определенным внутри макроса, делая их недоступными где-либо еще. esc отменит эти префиксы, чтобы имена в конечном выражении были точными, что вы пишете. Поэтому помните, что используйте только esc в том случае, если вам нужно получить доступ к этим переменным вне макроса, что не относится к вашему примеру.

  1. используется macroexpand для помощи в создании макросов

например, если вы разместили esc не в том месте, как показано ниже:

 macro m(e)
    quote
        function f(x)
            return $(esc(e))
        end
    end
end
 

вы получите такое выражение:

 julia> macroexpand( :( @m x 1 ) )
quote  # REPL[9], line 3:
    function #1#f(#2#x) # REPL[9], line 4:
        return x   1
    end
end
 

Вы можете обнаружить, что аргумент f и его аргумент имеют префикс специальной вещи (поскольку они не экранируются), в то время как тело просто x , которое нигде не определено.

  1. лучше использовать функции, а не макросы

Как правило, лучше использовать функции более высокого порядка для реализации «шаблонов функций», поскольку они легче читаются и могут использовать современные инструменты статического анализа — хотя, похоже, у Джулии их еще не было :). Запись сигнатур функций и их передача кажутся раздражающими, но они того стоят.

Комментарии:

1. «Запись сигнатур функций и их передача кажутся раздражающими, но они того стоят». Как всегда, зависит от того, что вы делаете.