# Using invoke() to call more general method

**URL:** <https://discourse.julialang.org/t/using-invoke-to-call-more-general-method/125324>\
**Category:** General Usage\
**Tags:** dispatch\
**Created:** [January 28, 2025, 11:58pm UTC](https://discourse.julialang.org/t/using-invoke-to-call-more-general-method/125324 "2025-01-28T23:58:59Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Ken\_Williams](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ken_williams/32/5289_2.png) [@Ken\_Williams](https://discourse.julialang.org/u/Ken_Williams)\
**Post date:** [January 28, 2025, 11:58pm UTC](https://discourse.julialang.org/t/using-invoke-to-call-more-general-method/125324/1 "2025-01-28T23:58:59Z")

</div>

I have a method for `convert()` that I use on various subtypes of a `ConfigStruct` type, to initialize from a dictionary. For a couple of subtypes, I want to define specific behaviors (e.g., catch deprecated keys and map them to their updated names), then call the general method. Here’s the idea, pared way down:

```julia
import Parameters: @with_kw

abstract type ConfigStruct end

@with_kw struct C1 <: ConfigStruct
    a::String
end

@with_kw struct C2 <: ConfigStruct
    b::String
end

function Main.convert(::Type{T}, d::Dict{Symbol}) where {T<:ConfigStruct}
    @info "Converting $T, generic"
    return T(; d...)
end

function Main.convert(::Type{T}, d::Dict{Symbol}) where {T<:C2}
    @info "Converting $T, specific"
    # ...convert, then call other method
    return invoke(convert, Tuple{Type{ConfigStruct},Dict{Symbol}}, (T, d))
end

s1::C1 = Dict(:a => "a")
s2::C2 = Dict(:b => "b")

```

Unfortunately, this usage of `invoke()` doesn’t work, I get this error on the final line of code:

```julia
[ Info: Converting C2, specific
ERROR: invoke: argument type error

```

I’ve tried several variations on that `invoke()`, does anyone have any guidance?

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [January 29, 2025, 12:38am UTC](https://discourse.julialang.org/t/using-invoke-to-call-more-general-method/125324/2 "2025-01-29T00:38:45Z")

</div>

This seems to work:

```julia
function Base.convert(::Type{T}, d::Dict{Symbol}) where {T<:C2}
    @info "Converting $T, specific"
    # ...convert, then call other method
    return invoke(convert, Tuple{Type{<:ConfigStruct}, Dict{Symbol}}, T, d)
end

```

* * *

However, I would probably just use constructors. This seems too cute by half:

```julia
c1::C1 = Dict(:a => "a")
c2::C2 = Dict(:b => "b")

```

This looks cleaner and clearer to me:

```julia
c1 = C1(a="a")
c2 = C2(b="b")

```

Regular Julia code rarely needs to invoke `invoke`.

---

<div class="post-metadata">

**Author:** ![cstjean](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cstjean/32/1444_2.png) [@cstjean](https://discourse.julialang.org/u/cstjean)\
**Post date:** [January 29, 2025, 12:56am UTC](https://discourse.julialang.org/t/using-invoke-to-call-more-general-method/125324/3 "2025-01-29T00:56:45Z")

</div>

Also of note, there’s a more convenient macro form since Julia 1.7:

```julia
help?> @invoke
  @invoke f(arg::T, ...; kwargs...)

  Provides a convenient way to call invoke by expanding @invoke f(arg1::T1, arg2::T2; kwargs...) to invoke(f, Tuple{T1,T2}, arg1, arg2; kwargs...). When an argument's type annotation is
  omitted, it's replaced with Core.Typeof that argument. To invoke a method where an argument is untyped or explicitly typed as Any, annotate the argument with ::Any.

  It also supports the following syntax:

    • @invoke (x::X).f expands to invoke(getproperty, Tuple{X,Symbol}, x, :f)

    • @invoke (x::X).f = v::V expands to invoke(setproperty!, Tuple{X,Symbol,V}, x, :f, v)

    • @invoke (xs::Xs)[i::I] expands to invoke(getindex, Tuple{Xs,I}, xs, i)

    • @invoke (xs::Xs)[i::I] = v::V expands to invoke(setindex!, Tuple{Xs,V,I}, xs, v, i)

  Examples
  ≡≡≡≡≡≡≡≡

  julia> @macroexpand @invoke f(x::T, y)
  :(Core.invoke(f, Tuple{T, Core.Typeof(y)}, x, y))
  
  julia> @invoke 420::Integer % Unsigned
  0x00000000000001a4
  
  julia> @macroexpand @invoke (x::X).f
  :(Core.invoke(Base.getproperty, Tuple{X, Core.Typeof(:f)}, x, :f))
  
  julia> @macroexpand @invoke (x::X).f = v::V
  :(Core.invoke(Base.setproperty!, Tuple{X, Core.Typeof(:f), V}, x, :f, v))
  
  julia> @macroexpand @invoke (xs::Xs)[i::I]
  :(Core.invoke(Base.getindex, Tuple{Xs, I}, xs, i))
  
  julia> @macroexpand @invoke (xs::Xs)[i::I] = v::V
  :(Core.invoke(Base.setindex!, Tuple{Xs, V, I}, xs, v, i))

```

---

<div class="post-metadata">

**Author:** ![Ken\_Williams](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ken_williams/32/5289_2.png) [@Ken\_Williams](https://discourse.julialang.org/u/Ken_Williams)\
**Post date:** [January 29, 2025, 5:27pm UTC](https://discourse.julialang.org/t/using-invoke-to-call-more-general-method/125324/4 "2025-01-29T17:27:10Z")

</div>

> [@CameronBieganek](#):
>
> This seems to work:
> 
> ```julia
> function Base.convert(::Type{T}, d::Dict{Symbol}) where {T<:C2}
> @info "Converting $T, specific"
> # ...convert, then call other method
> return invoke(convert, Tuple{Type{<:ConfigStruct}, Dict{Symbol}}, T, d)
> end
> 
> ```

Ahh thanks, I don’t know why I thought the arguments themselves also had to be in a tuple.

The following also works and I’ll use it for my general case:

```julia
invoke(convert, Tuple{Type{<:supertype(T)},typeof(d)}, T, d)

```

> [@CameronBieganek](#):
>
> However, I would probably just use constructors.

Yes, I usually would too. I’m using this technique in a context where the `Dict` values come from a TOML file, and they need to be recursively converted to the types that the rest of the code will use. It’s turned out to be a lot cleaner to do this implicitly by declaration of types in the `ConfigStruct` subtypes, rather than a lot of explicit conversion code that “knows” the details of those structures.

---

<div class="post-metadata">

**Author:** ![Ken\_Williams](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ken_williams/32/5289_2.png) [@Ken\_Williams](https://discourse.julialang.org/u/Ken_Williams)\
**Post date:** [January 29, 2025, 6:05pm UTC](https://discourse.julialang.org/t/using-invoke-to-call-more-general-method/125324/5 "2025-01-29T18:05:18Z")

</div>

> [@cstjean](#):
>
> Also of note, there’s a more convenient macro form since Julia 1.7:

Cool, thanks, that means I can change

```julia
invoke(convert, Tuple{Type{<:supertype(T)},typeof(d)}, T, d)

```

to

```julia
@invoke convert(T::Type{<:supertype(T)}, d)

```

, which reads a lot better.
