# Invoke different method for callable struct / How to emulate pythons \`super()\`

**URL:** <https://discourse.julialang.org/t/invoke-different-method-for-callable-struct-how-to-emulate-pythons-super/57869>\
**Category:** General Usage\
**Tags:** question, inheritance, function, parametric-methods\
**Created:** [March 24, 2021, 2:06pm UTC](https://discourse.julialang.org/t/invoke-different-method-for-callable-struct-how-to-emulate-pythons-super/57869 "2021-03-24T14:06:04Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![jlbosse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlbosse/32/11274_2.png) [@jlbosse](https://discourse.julialang.org/u/jlbosse)\
**Post date:** [March 24, 2021, 2:06pm UTC](https://discourse.julialang.org/t/invoke-different-method-for-callable-struct-how-to-emulate-pythons-super/57869/1 "2021-03-24T14:06:04Z")

</div>

I have a parametric function as discussed [here](https://discourse.julialang.org/t/function-factories-or-callable-structs/52987) and now want to generate another such function type that logs its arguments while invoking the non-logging method for the computation. How do I use `invoke` to dispatch on the type of `f` and not on `argtypes`?

If that is all a bit convoluted, here is a MWE

```julia
abstract type AbstractAdder <: Function end

# the non-logging version
struct Adder <: AbstractAdder
    a::Int
end

# general implementation for all adders
function (f::AbstractAdder)(x::Int)
    return f.a + x
end

# the logging version
struct TrackingAdder <: AbstractAdder 
    a::Int
    log::Vector{Int}
end

# special implementation for the logging version.
# It should just push `x` to `f.log` and then call the general
# method to do the actual computation
function (f::TrackingAdder)(x::Int)
    push!(f.log, x)
    # how to use `invoke` here s.t. it uses the general method for `AbstractAdder`?
    return invoke((), Tuple{AbstractAdder, Int}, x)
end

```

---

<div class="post-metadata">

**Author:** ![jlbosse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlbosse/32/11274_2.png) [@jlbosse](https://discourse.julialang.org/u/jlbosse)\
**Post date:** [March 24, 2021, 4:30pm UTC](https://discourse.julialang.org/t/invoke-different-method-for-callable-struct-how-to-emulate-pythons-super/57869/2 "2021-03-24T16:30:21Z")

</div>

While I am still waiting for answers, here is what I do for now:

```julia
abstract type AbstractAdder <: Function end

# the non-logging version
struct Adder <: AbstractAdder
    a::Int
end

@inline function _call(f::AbstractAdder, x::Int)
    return f.a + x
end

(f::AbstractAdder)(x::Int) = _call(f, x)

# the logging version
struct TrackingAdder <: AbstractAdder 
    a::Int
    log::Vector{Int}
end

function (f::TrackingAdder)(x::Int)
    push!(f.log, x)
    return _call(f, x)
end

```

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [March 24, 2021, 4:43pm UTC](https://discourse.julialang.org/t/invoke-different-method-for-callable-struct-how-to-emulate-pythons-super/57869/3 "2021-03-24T16:43:40Z")

</div>

I think that you could add dummy unused singleton arguments to either the struct or the abstract type function or both. So you ignore the value, and just let Julia use the type of the arg for dispatch.

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [March 24, 2021, 4:51pm UTC](https://discourse.julialang.org/t/invoke-different-method-for-callable-struct-how-to-emulate-pythons-super/57869/4 "2021-03-24T16:51:24Z")

</div>

So something like this:

```julia
abstract type AbstractAdder <: Function end

abstract type DisambigSingleton end
struct DisambigAbstractAdder <: DisambigSingleton end
struct DisambigTrackingAdder <: DisambigSingleton end

# the non-logging version
struct Adder <: AbstractAdder
        a::Int
end

# general implementation for all adders
function (f::AbstractAdder)(x::Int, ::DisambigAbstractAdder)
        return f.a + x
end

# the logging version
struct TrackingAdder <: AbstractAdder
        a::Int
        log::Vector{Int}
end

# special implementation for the logging version.
# It should just push `x` to `f.log` and then call the general
# method to do the actual computation
function (f::TrackingAdder)(x::Int, ::DisambigTrackingAdder)
        push!(f.log, x)

        return invoke(f, Tuple{Int, DisambigAbstractAdder}, x, DisambigAbstractAdder())
end

```

REPL usage example:

```julia
               _
   _ _ _(_)_ | Documentation: https://docs.julialang.org
  (_) | (_) (_) |
   _ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` | |
  | | |_| | | | (_| | | Version 1.5.4 (2021-03-11)
 _/ |\ __'_|_|_|\__'_| |
|__/ |

julia> include("sup.jl")

julia> a = TrackingAdder(5, Int[])
(::TrackingAdder) (generic function with 2 methods)

julia> a(3, DisambigTrackingAdder())
8

julia> a.log
1-element Array{Int64,1}:
 3

```

I think it would be possible to make this prettier with some wrappers, but I’m guessing you’d do best to adopt a different approach. There is probably a more “Julian” solution to your problem.

EDIT: I think an improvement might be to make `DisambigSingleton` a parametric `struct`, parameterized by, e.g., `Int`, then the “super” operation could perhaps be more automatic (by doing something like `DisambigSingleton{N + 1}` (or `DisambigSingleton{N - 1}`) in the methods instead of super.

---

<div class="post-metadata">

**Author:** ![jlbosse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlbosse/32/11274_2.png) [@jlbosse](https://discourse.julialang.org/u/jlbosse)\
**Post date:** [March 24, 2021, 5:01pm UTC](https://discourse.julialang.org/t/invoke-different-method-for-callable-struct-how-to-emulate-pythons-super/57869/5 "2021-03-24T17:01:46Z")

</div>

Yep, that works. I modified the idea a bit more by making the singleton a default argument

```julia
function (f::AbstractAdder)(x::Int, _::DisambigAbstractAdder=DisambigAbstractAdder())
        return f.a + x
end

function (f::TrackingAdder)(x::Int, _::DisambigTrackingAdder=DisambigTrackingAdder())
        push!(f.log, x)
        return invoke(f, Tuple{Int, DisambigAbstractAdder}, x, DisambigAbstractAdder())
end

```

because having to pass the singleton manually pretty much defeats the purpose of the whole exercise.
