the ability to mark functions as deprecated by the macros

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.

Leave a Comment

Your email address will not be published.

Scroll to Top