Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider finalizer(x,f) to return x rather than nothing #22018

Closed
m-j-w opened this issue May 22, 2017 · 13 comments
Closed

Consider finalizer(x,f) to return x rather than nothing #22018

m-j-w opened this issue May 22, 2017 · 13 comments
Assignees
Labels
good first issue Indicates a good issue for first-time contributors to Julia status:help wanted Indicates that a maintainer wants help on an issue or pull request
Milestone

Comments

@m-j-w
Copy link
Contributor

m-j-w commented May 22, 2017

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.

@StefanKarpinski
Copy link
Sponsor Member

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.

@yuyichao
Copy link
Contributor

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 #16307 in another issue that's proposing a non-breaking change.

@StefanKarpinski
Copy link
Sponsor Member

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

@yuyichao
Copy link
Contributor

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.

@yuyichao
Copy link
Contributor

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.

@vtjnash
Copy link
Sponsor Member

vtjnash commented May 22, 2017

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)

@yuyichao
Copy link
Contributor

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

@andyferris
Copy link
Member

andyferris commented May 23, 2017

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.

@vtjnash vtjnash added this to the 1.0 milestone Sep 14, 2017
@vtjnash vtjnash self-assigned this Sep 14, 2017
@vtjnash
Copy link
Sponsor Member

vtjnash commented Sep 14, 2017

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!

@vtjnash
Copy link
Sponsor Member

vtjnash commented Sep 19, 2017

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

@StefanKarpinski
Copy link
Sponsor Member

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

@StefanKarpinski StefanKarpinski added status:triage This should be discussed on a triage call good first issue Indicates a good issue for first-time contributors to Julia status:help wanted Indicates that a maintainer wants help on an issue or pull request labels Nov 20, 2017
@StefanKarpinski
Copy link
Sponsor Member

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

@StefanKarpinski StefanKarpinski removed the status:triage This should be discussed on a triage call label Nov 20, 2017
@StefanKarpinski
Copy link
Sponsor Member

This is done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Indicates a good issue for first-time contributors to Julia status:help wanted Indicates that a maintainer wants help on an issue or pull request
Projects
None yet
Development

No branches or pull requests

5 participants