Functor abstract types

Given a concrete type, one can define functor-like methods with:

struct Foo end
(f::Foo)(x) = 2x

and use the object as a function:

f = Foo()
f(1)

This doesn’t work with abstract types, however; even though they support abstract methods:

abstract type A end
iscool(::A) = false # works just fine
(::A)(x) = 2x # fails, cannot add method to abstract type

Is there any way to define functor-like abstract types? I am just trying to avoid typing the same function for all subtypes of the abstract type manually.

1 Like
abstract type A end
struct Foo <: A end
struct Goo <: A end

iscool(::A) = false

f = Foo()
g = Goo()

julia> iscool(f)
false

julia> iscool(g)
false


(::Type{T})(x) where {T<:A} = 232

Foo(1) == 232
Goo(1) == 232

edit: I think I misunderstood the original question.

1 Like

Thanks, doesn’t this syntax with where defines the function for all subtypes without the possibility of specialization? I forgot to say that I need to very rarely overwrite the definition for specific subtypes.

I have encountered this before and also did not find a solution I am happy with. We basically work around this using something like

for T in union(subtypes(DistanceLoss), subtypes(MarginLoss))
    @eval (loss::$T)(args...) = value(loss, args...)
end

but this has a serious drawback (among others) that it only works for subtypes defined before that loop is executed (which is why its the last block of code in the package)

As a side note, fallbacks do work for parametric types

julia> immutable Foo{N} end

julia> (::Foo)(x) = 2x

julia> Foo{2}()(3)
6

julia> Foo{3}()(3)
6
3 Likes

Currently no. https://github.com/JuliaLang/julia/issues/14919

2 Likes

Thank you for sharing the issue @yuyichao, I will keep the code as is for now then.

Thanks @Evizero for the workaround, I will keep writing things manually, at least for now when it is still manageable.

Wikipedia have an interesting article about “function object” (often called functor)

Otherwise there is no example for Julia.

Maybe someone could provide an example there?

I also noticed that https://github.com/lindahua/NumericFuns.jl provide functors… but it’s broken with Julia 0.6 see https://github.com/lindahua/NumericFuns.jl/issues/9
Any news about it?

PS: Sorry, I should have post there https://discourse.julialang.org/t/help-defining-functor-type/

This is a classic example of a closure with state:

function make_counter()
    n = 0
    function()
        n += 1
    end
end

c = make_counter()

[c() for _ in 1:10] # will collect numbers from 1 to 10

But you can also get the same behaviour by using a mutable struct and making it callable:

mutable struct Counter
    n::Int
end

Counter() = Counter(0)

(c::Counter)() = c.n += 1

c = Counter()

[c() for _ in 1:10]

Arguably, the second one is easier to inspect, extend, and debug.

1 Like

Thanks @Tamas_Papp I’ve just edit Wikipedia page “Function object” to add a Julia example with an accumulator implementation (both with mutable struct and with closure with state).
This example is inspired from Python example

Nice addition. “Such a accumulator” should be “Such an accumulator”

Fixed. Thanks @ggggggggg for catching this