I think what you want to achieve is not the same thing as what [email protected]
does. If I understand correctly:
- you don’t want the macro to create a definition for the deprecated method; you want rather to annotate a handwritten definition
- you want to modify docstrings, which
@deprecate
does not
And since you’re doing this as an exercise to learn metaprogramming, maybe you could try writing your own macro step by step, rather than understanding how [email protected]
works and trying to adapt it.
As for your specific questions:
1. How do I deal with a situation when the caller doesn’t import Markdown?
Maybe the following example helps explaining how things work:
module MyModule
# Markdown.MD, Markdown.Paragraph and msg are only available from this module
import Markdown
msg(name) = "Hello $name"
macro greet(name)
quote
# function names (e.g. Markdown.MD or msg) are interpolated
# => evaluated at macro expansion time in the scope of the macro itself
# => refer to functions available from within the module
$(Markdown.MD)($(Markdown.Paragraph)($msg($name)))
# (But these functions are not called at macro expansion time)
end
end
end
See in particular how msg
correctly refers to Main.MyModule.msg
, which is how you have to call it from the “outside” context:
julia> @macroexpand [email protected] "John"
quote
#= REPL[8]:8 =#
(Markdown.MD)((Markdown.Paragraph)((Main.MyModule.msg)("John")))
end
julia> [email protected] "John"
Hello John
2. Perhaps the way to do it is to only allow adding the @mark_deprecated to the actual function definition?
Yes, that is what I would do.
3. Is it possible to default application of the macro to the bare function symbol to deprecating all methods?
I guess it would technically be possible to deprecate all methods of a given function… or at least all methods that exist at the time when your deprecation code runs. But what about methods that would be defined afterwards? I personally would not go that way, marking only method definitions.
Maybe something like this could be a stub to be used as a starting point for a more complex macro doing precisely what you want:
module MarkDeprecate
using Markdown
using MacroTools
function mark_docstring(docstring, message)
push!(docstring,
Markdown.Paragraph("Warning: this method is deprecated! $message"))
docstring
end
function warn_if_necessary(message)
@warn "This method is deprecated! $message"
end
macro mark_deprecate(msg, expr)
fundef = splitdef(expr)
prototype = :($(fundef[:name])($(fundef[:args]...);
$(fundef[:kwargs]...)) where {$(fundef[:whereparams]...)})
fundef[:body] = quote
$warn_if_necessary($msg)
$(fundef[:body])
end
quote
Base.@__doc__ $(esc(MacroTools.combinedef(fundef)))
[email protected] $mark_docstring(@doc($prototype), $msg) $prototype
end
end
end
julia> """
bar(x::Number)
some help
"""
[email protected]_deprecate "Use foo instead" function bar(x::Number)
42
end
bar
julia> """
bar(s::String)
This one is not deprecated
"""
bar(s::String) = "not deprecated"
bar
julia> methods(bar)
# 2 methods for generic function "bar":
[1] bar(s::String) in Main at REPL[4]:6
[2] bar(x::Number) in Main at REPL[1]:23
julia> @doc(bar)
bar(x::Number)
some help
Warning: this method is deprecated! Use foo instead
bar(s::String)
This one is not deprecated
julia> bar("hello")
"not deprecated"
julia> bar(5)
┌ Warning: This method is deprecated! Use foo instead
└ @ Main.MarkDeprecate REPL[1]:12
42
CLICK HERE to find out more related problems solutions.