Julia: Consider finalizer(x,f) to return x rather than nothing

Created on 22 May 2017  路  13Comments  路  Source: JuliaLang/julia

Currently, finalizer returns nothing, which requires simple constructors to be more verbose than necessary:

mutable struct A
    v
    A(x) = (a = new(x); finalizer(a, fn) ; a)
end

If finalizer(x, f) where to return the object x instead:

mutable struct B
    v
    B(x) = finalizer(new(x), fn)
end

I think this would improve readability for simple constructors.

good first issue help wanted

Most helpful comment

Except that we put functions as the first argument everywhere else.

All 13 comments

Also, let's swap the arguments: finalizer(f, x). Of course, technically that's breaking since you could put a finalizer on a function, but it seems unlikely to come up in practice.

finalizer is doing something to the object so it also makes sense to put the object it operates on as the first argument. Also I don't think it's very useful to bring up a breaking change that's already have it's own issue https://github.com/JuliaLang/julia/issues/16307 in another issue that's proposing a non-breaking change.

Except that we put functions as the first argument everywhere else.

Except that we put functions as the first argument everywhere else.

No. We do that when there's nothing else that has any reason to be in the first position, which is not the case here. A quick search shows other functions where we are not doing that includes sparsevec.

Also, finalizer is one of the few cases it should be strongly discouraged to use an anonymous function as input since that means it'll be very hard to do explicit cleanup. finalizer also accepts pointer as the callback which does not make sense to be the first argument.

Also, finalizer is one of the few cases it should be strongly discouraged to use an anonymous function as input

maybe we should add a one-arg form?

finalizer(g) = (finalizer(g, finalize); g)

finalize is never the right function to be the finalizer though...

Is there any chance of having type-based finalizers (#1037) rather than per-object ones (by v1.0)? I remember discussions about this in the past, but I don't remember if there was a consensus.

Proposal from triage:

  • deprecate finalize
  • swap argument order to finalizer
  • return object from finalizer
  • (eventually) add one-arg form to finalizer for "type-based finalization", which calls a default function, finalize!

API demo:

"""
    destroy!(x)

Default method for `ondestroy(x)`.
"""
function destroy! end

"""
    ondestroy(finalize::Any, @nospecialize x)

Calls `finalize(x)` when no references to `x` exist.
"""
function ondestroy(@nospecialize(finalize), @nospecialize x)
    ccall(:jl_register_finalizer, Void, (Any, Any), x, finalize)
    return x
end

"""
    ondestroy(x)

Calls `destroy!(x)` when no references to `x` exist.
"""
ondestroy(@nospecialize x) = ondestroy(destroy!, x)

@deprecate finalizer(x, f) ondestroy(f, x)
@noinline function finalize(x)
    depwarn("Manual execution of all finalizers is deprecated. Use type-based finalizer method `destroy!` instead.`, :finalize)
    <original implementation>
end

Exploratory usage examples:

struct T
    fields...
    function T(fields...)
        return ondestroy(new(fields...)) do
            @async println("finalized object")
        end
    end
    T() = ondestroy(new())
    Base.destroy!(::T) = @async println("default finalizer called on object")
end
destroy!(T())

Other names considered: oncleanup / cleanup, defer / deferred / deferred!, ondelete

The originally stated proposal of this issue is non-breaking since it's rather unlikely that anyone would be relying on finalizer returning nothing.

OTOH this is easy to do and why not return the object?

This is done.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

musm picture musm  路  3Comments

Keno picture Keno  路  3Comments

StefanKarpinski picture StefanKarpinski  路  3Comments

TotalVerb picture TotalVerb  路  3Comments

sbromberger picture sbromberger  路  3Comments