You won’t need to directly use Markdown.parse(docstr), since that’s what 
julia does for you anyway, see here 
<https://github.com/JuliaLang/julia/blob/baf174f5c252a438006c6f23ed7097fbab004025/base/docs/Docs.jl#L437-L443>,
 
where any plain string or interpolated string expression gets wrapped in a 
markdown parsing call automatically when passed to @doc.

Also, note that defining new variables inside the returned block, in this 
case dot_op and docstr, will be visible (though gensym’d) from the calling 
env, ie.

macro foo_op(op)
    quote
        dot_op = symbol(".$($op)")
        docstr = "`$($op)` also works for `Foo` types!"
        function Base.$op(a::Foo, b::Foo)

            Foo(dot_op(a.x, b.x))
        end
        @doc Markdown.parse(docstr) Base.$op
        Base.$op
    end
end

julia> @foo_op +

julia> names(Main)
10-element Array{Symbol,1}:
 symbol("#3#dot_op")        
 symbol("#4#docstr")        
 symbol("@foo_op")
 :Base                      
 :Core                      
 :Foo                       
 :Main                      
 :User                      
 :__META__                  
 :ans

Notice symbol("#3#dot_op") and symbol("#4#docstr"). Each time you call the 
macro you’ll generate a new pair. They won’t clash with any other 
variables, but it’s worth trying to avoid creating them to keep the 
namespace clean. Both the variables aren’t really needed anyway since docstr's 
value could just be placed directly into the @doc.

The other variable, dot_op, might actually cause some trouble 
(performance-wise) since you’re defining a non-const global that aliases a 
function and then calling it from inside a function. Compare the output of 
the @code_* macros for your macro and the following
version that doesn’t define temporary variables:

julia> macro foo_op_direct(op)
           quote
               function Base.$op(a::Foo, b::Foo)
                   Foo($(symbol(".", op))(a.x, b.x))
               end
               @doc "`$($op)` also works for `Foo` types!" Base.$op
               Base.$op
           end
       end

julia> type Foo
           x :: Int
       end

julia> @foo_op_direct +
+ (generic function with 166 methods)

julia> code_llvm(+, (Foo, Foo))
...

The code generated by @foo_op_direct will be quite a bit shorter than that 
of @foo_op.

— Mike
​

On Thursday, 17 December 2015 07:16:09 UTC+2, Ismael Venegas Castelló wrote:
>
> I didn't know of "$($x)" to interpolate inside stirng inside expresion, 
> thanks!
>
> This also works:
>
> macro foo_op(op)
>     quote
>         dot_op = symbol(".$($op)")
>         docstr = "`$($op)` also works for `Foo` types!"
>         function Base.$op(a::Foo, b::Foo)
>             Foo(dot_op(a.x, b.x))
>         end
>         @doc Markdown.parse(docstr) Base.$op
>         Base.$op
>     end
> end
>
> El miércoles, 16 de diciembre de 2015, 11:19:24 (UTC-6), Michael Hatherly 
> escribió:
>>
>> If the macro returns a single documentable expression, such as a function 
>> or type, then you shouldn’t need to do anything special, just add an @doc 
>> to the for-loop that generates the methods.
>>
>> If the macro returns a block expression then you need to tell the 
>> docsystem how you’d like to handle it. Use Base.@__doc__ to mark the 
>> subexpression you’d like to document in expression returned by the macro. 
>> See this section of the manual, 
>> http://docs.julialang.org/en/latest/manual/documentation/#macro-generated-code,
>>  
>> for an example. Something like the following should work for the code in 
>> this thread:
>>
>> macro operator_obs(name)
>>     M = esc(:M)
>>     n = esc(name)
>>     d = esc(symbol(".", name))
>>     quote
>>         import Base: $n
>>         Base.@__doc__ $(n)(m_1::$M, m_2::$M) = $(M)($(d)(m_1.a, m_2.a), 
>> $(d)(m_1.b, m_2.b))
>>     end
>> end
>>
>> for op in [:+, :-, :*, :/]
>>     @eval begin
>>         @doc "`$($op)` docs for type `M`." ->
>>         @operator_obs $op
>>     end
>> end
>>
>> help?> M(1, 2) + M(2, 3)
>>   + docs for type M.
>>
>> help?> M(1, 2) / M(2, 3)
>>   / docs for type M.
>>
>> Documenting code generated by a function, rather than a macro, isn’t 
>> going to be as clean though. You’d need to use @doc after defining the 
>> code, perhaps something like:
>>
>> for op in [:+, :-, :*, :/]
>>     @eval begin
>>         operator_obs($op)
>>         @doc "`$($op)` docs for type `M`." $(op)(::M, ::M)
>>     end
>> end
>>
>> to add docs to the correct op method.
>>
>> — Mike
>> ​
>>
>>
>> On Wednesday, 16 December 2015 17:41:36 UTC+2, j verzani wrote:
>>>
>>> While this topic is active, can anyone suggest how this can be 
>>> incorporated with docstrings to add help entries to the newly generated 
>>> methods?
>>>
>>> On Wednesday, December 16, 2015 at 5:22:24 AM UTC-5, Greg Plowman wrote:
>>>>
>>>> I have exactly the same requirement. 
>>>> Additionally, I often have more than 2 fields and also change the 
>>>> fields of my custom types.
>>>>
>>>> So I use a slightly more general version of the above:
>>>>
>>>>
>>>> function CompositeBinaryOp(T::Symbol, op::Symbol)
>>>>     expressions = [ :($op(x1.$field, x2.$field)) for field in 
>>>> fieldnames(eval(T)) ]
>>>>     body = Expr(:call, T, expressions...)
>>>>     quote
>>>>         function $op(x1::$T, x2::$T)
>>>>             return $body
>>>>         end
>>>>     end
>>>> end
>>>>
>>>> type M
>>>>     a
>>>>     b
>>>> end
>>>>
>>>> import Base: +, -, *, /, ^
>>>>
>>>> for T in [:M]
>>>>     for op in [:+, :-, :*, :/, :^]
>>>>         #eval(CompositeBinaryOp(T, op))
>>>>
>>>>         code = CompositeBinaryOp(T, op)
>>>>         println(code, "\n")
>>>>         eval(code)
>>>>     end
>>>> end
>>>>
>>>>  
>>>> The advantage here for me is that I can change the fields (number, type 
>>>> order etc.) and operators don't need manual updating.
>>>>
>>>> I have similar functions for copy constructors, unary operators etc.
>>>>
>>>

Reply via email to