Why can't it find that method?

I normally post to Slack, but seems too long winded for that venue.

I’m having some trouble that might be related to a macro I’ve defined, but as near as I can tell the macro expands as I expect.

I’m hoping you can help me figure out what’s going wrong.

Here’s the relevant output snippet from my unit tests (it explicitly lists the mehods for debugging):

# 3 methods for generic function "install" from Rete:
 [1] install(root::BasicReteNode, ::JoinSequentialLetterDigram2)
     @ Main C:\Users\Mark Nahabedian\.julia\dev\Rete\src\rules.jl:56
 [2] install(root::BasicReteNode, ::JoinSequentialLetterTrigram2)
     @ Main C:\Users\Mark Nahabedian\.julia\dev\Rete\src\rules.jl:56
 [3] install(root::BasicReteNode, rule_group)
     @ C:\Users\Mark Nahabedian\.julia\dev\Rete\src\basic_node.jl:32
rule example 2: Error During Test at C:\Users\Mark Nahabedian\.julia\dev\Rete\test\rule_example_2.jl:14
  Got exception outside of a @test
  MethodError: no method matching install(::BasicReteNode, ::JoinSequentialLetterDigram2)
  Stacktrace:
    [1] macro expansion
      @ C:\Users\Mark Nahabedian\.julia\dev\Rete\test\rule_example_2.jl:17 [inlined]

As near as I can tell, the method it claims is missing is the first on the list. It results from (line numbers elided for readability):

@macroexpand @rule JoinSequentialLetterDigram2(a::Char, b::Char) begin
    if codepoint(a) + 1 == codepoint(b)
        emit((a, b))
    end
end
quote
    struct JoinSequentialLetterDigram2 <: Rule
    end
    function Rete.install(root::BasicReteNode, ::JoinSequentialLetterDigram2)
        join = JoinNode("JoinSequentialLetterDigram2", JoinSequentialLetterDigram2())
        connect_a(ensure_IsaMemoryNode(root, Char), join)
        connect_b(ensure_IsaMemoryNode(root, Char), join)
        connect(join, root)
    end
    function (::JoinSequentialLetterDigram2)(node::JoinNode, a::Char, b::Char)
        begin
            if codepoint(a) + 1 == codepoint(b)
                emit(node, (a, b))
            end
        end
    end
end

Here’s the macro. Most of it is parameter decoding. I use esc because I don’t think I’m using any identifiers that require alphatization and, in my observations, esc does something completely random. Maybe Dave Moon’s fixes will be incorporated in my lifetime, but I’m not holding my breath.

macro rule(call, body)
    if !isexpr(call, :call)
        error("The first expression of rule should look like a call")
    end
    supertype = Rule
    rule_name = call.args[1]
    if isexpr(rule_name, :(.))
        supertype = rule_name.args[1]
        rule_name = rule_name.args[2].value  # Unwrap QuoteNode
    end
    rule_name_str = string(rule_name)
    @assert isexpr(call.args[2], :(::))
    @assert isexpr(call.args[3], :(::))
    a_var = call.args[2].args[1]
    a_type = call.args[2].args[2]
    b_var = call.args[3].args[1]
    b_type = call.args[3].args[2]
    # Add the node argument to all calls to emit:
    body = postwalk(body) do e
        if isexpr(e, :call) && e.args[1] == :emit
            Expr(:call, :emit, :node, e.args[2])
        else
            e
        end
    end
    
    esc(quote
        struct $rule_name <: $supertype end

        function Rete.install(root::BasicReteNode, ::$rule_name)
            join = JoinNode($rule_name_str, $rule_name())
            connect_a(ensure_IsaMemoryNode(root, $a_type), join)
            connect_b(ensure_IsaMemoryNode(root, $b_type), join)
            connect(join, root)
        end

        function(::$rule_name)(node::JoinNode,
                               $a_var::$a_type,
                               $b_var::$b_type)
            $body
        end
    end)
end

Thanks.

I tink I found the problem, but I don’t understand why.

install == Rete.install is false.

Rete exports install. The tunit test environment imports Rete. What’s going wrong?

And the answer is …

Another test was defining install methods without prefixing them. That’s how there got to be two versions of `install.

3 Likes