2

Since version 1.3 Julia allows functor dispatch on abstract types. Therefore, I was wondering whether it is possible to explicitly invoke the parent functor from a child object.

E.g. in the example below, is there any way to call (x::Foo)() through the bar::Bar object?

abstract type Foo end
(x::Foo)() = "Invoked Foo functor."

struct Bar <: Foo end
(x::Bar)() = "Invoked Bar functor."

bar = Bar()
@info "Calling bar functor directly."
bar() |> println
@info "Invoking parent functor."
# ??? - how to invoke parent functor (x::Foo)() (e.g. desired output "Invoked Foo functor")
invoke(bar,Tuple{Bar}, bar) |> println
phipsgabler
  • 20,535
  • 4
  • 40
  • 60
lassepe
  • 301
  • 1
  • 8
  • 1
    any reason why you don't just want to define `function x(y :: Foo)... end`? – Oscar Smith Oct 25 '19 at 20:32
  • No, I could totally replace it with a function (as any functionality that is offered by functors could be provided by a simple function instead). I was just wondering whether there is syntactic sugar to do this or whether I just have to stick to the workaround you suggested. – lassepe Oct 25 '19 at 21:04
  • 2
    in Julia functions are much more idiomatic than defining a functor and messing with dispatch – Oscar Smith Oct 25 '19 at 21:08
  • This isn't immediately (or even non-immediately) possible as `invoke` is a builtin. Now, what you might be able to do is ccall into Julia's internals at the proper point. It'll just take some munging to essentially do [this](https://github.com/JuliaLang/julia/blob/f8067172557e4967714e92450f27d74dbf959760/src/gf.c#L2332-L2351) — and as a hack into an unofficial API it'll be prone to changes in Julia. – mbauman Oct 25 '19 at 21:39

1 Answers1

3

what about using a default struct?

abstract type Foo end
(x::Foo)() = "Invoked Foo functor."

struct Bar <: Foo end
(x::Bar)() = "Invoked Bar functor."

struct DefaultFoo <: Foo end
#here we dont define an specialized method, DefaultFoo calls Foo

#an interface to define default methods: 
default(x::Type{Foo}) = DefaultFoo


function parent(x::T) where {T}
     y = default(supertype(T))
     return y()
end

finally, you can do this to call the default function:

bar = Bar()
foo = parent(bar)
foo()

this requires a definition of a defaultFoo type and a default(x::Type{T}) for each supertype . you can automate this with the following macro:

macro create_default_functor(type)
    a = gensym(type)
    esc(quote
        struct $a <: $type end
        default(x::Type{$type})  = $a
    end)
end

using the macro, and your code:

abstract type Foo end
(x::Foo)() = "Invoked Foo functor."
@create_default_functor Foo
struct Bar <: Foo end
(x::Bar)() = "Invoked Bar functor."

function parent(x::T) where {T}
     y = default(supertype(T))
     return y()
end

#calling bar
bar = Bar()
bar()
#calling foo
foo = parent(bar)
foo()

I don't have the macro knowledge right now to call the macro directly on the abstract type definition, but is a start. The thing about abstract functors is that is a very new feature (1.3 is not out yet) and maybe this can be added on future versions of julia (something like call_parent(Foo,args...), ) if you add a PR suggesting the feature.

mu7z
  • 466
  • 5
  • 14
longemen3000
  • 1,273
  • 5
  • 14