Functor abstract types

question
type

#1

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.


#2
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.


#3

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.


#4

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

#5

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


#6

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


#7

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


#8

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/


#9

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.


#10

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


#11

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


#12

Fixed. Thanks @ggggggggg for catching this