Library support for go-like deferred function #1523

Closed
brson opened this Issue Jan 14, 2012 · 7 comments

Comments

Projects
None yet
3 participants
@brson
Contributor

brson commented Jan 14, 2012

I recently wrote the following to get some behavior like a 'finally' block:

        resource finally(ch: comm::chan<monitor_msg>) {                                                                                            
            comm::send(ch, done);                                                                                                                  
        }                                                                                                                                          

        let _finally = finally(ch);

What I really wanted to write was:

defer {||
    comm::send(ch, done);
}

Or something structured like try/finally.

I tried to devise some way to write a defer library function but there doesn't seem to be any safe way to do it since blocks can't be put into resources.

@brson

This comment has been minimized.

Show comment
Hide comment
@brson

brson Jan 14, 2012

Contributor

Maybe there's some way to build something that looks like try finally. It's not safe to put a block into a resource, but we could do it unsafely if we can guarantee the resource doesn't escape. Something that looks vaguely like:

try {||
  ... something
} finally {||
  ... always run this
}

Could be implemented.

Contributor

brson commented Jan 14, 2012

Maybe there's some way to build something that looks like try finally. It's not safe to put a block into a resource, but we could do it unsafely if we can guarantee the resource doesn't escape. Something that looks vaguely like:

try {||
  ... something
} finally {||
  ... always run this
}

Could be implemented.

@brson

This comment has been minimized.

Show comment
Hide comment
@brson

brson Jan 14, 2012

Contributor

So this works:

impl finally for fn@() {                                                                                                                           
    fn finally(f: block()) {                                                                                                                       
        resource r(f: fn@()) {                                                                                                                     
            f();                                                                                                                                   
        }                                                                                                                                          
        unsafe {                                                                                                                                   
            let _r = r(unsafe::reinterpret_cast(f));                                                                                               
            self();                                                                                                                                
        }                                                                                                                                          
    }                                                                                                                                              
}                                                                                                                                                  

fn main() {                                                                                                                                        
    let a = @"toast";                                                                                                                              
    fn@() {                                                                                                                                        
        log(error, "test");                                                                                                                        
    }.finally {||                                                                                                                                  
        log(error, a);                                                                                                                             
    }                                                                                                                                              
}  

The only problem is that the try block is a fn@ not a fn& because of limitations on where you can write fn&.

Contributor

brson commented Jan 14, 2012

So this works:

impl finally for fn@() {                                                                                                                           
    fn finally(f: block()) {                                                                                                                       
        resource r(f: fn@()) {                                                                                                                     
            f();                                                                                                                                   
        }                                                                                                                                          
        unsafe {                                                                                                                                   
            let _r = r(unsafe::reinterpret_cast(f));                                                                                               
            self();                                                                                                                                
        }                                                                                                                                          
    }                                                                                                                                              
}                                                                                                                                                  

fn main() {                                                                                                                                        
    let a = @"toast";                                                                                                                              
    fn@() {                                                                                                                                        
        log(error, "test");                                                                                                                        
    }.finally {||                                                                                                                                  
        log(error, a);                                                                                                                             
    }                                                                                                                                              
}  

The only problem is that the try block is a fn@ not a fn& because of limitations on where you can write fn&.

@nikomatsakis

This comment has been minimized.

Show comment
Hide comment
@nikomatsakis

nikomatsakis Jan 15, 2012

Contributor

It seems to me that we should just define an inline resource syntax. Admittedly, the "finally" code then appears at the top---but that kind of makes sense to me. D has something similar: a way to say, "this code will run on all exits"

Contributor

nikomatsakis commented Jan 15, 2012

It seems to me that we should just define an inline resource syntax. Admittedly, the "finally" code then appears at the top---but that kind of makes sense to me. D has something similar: a way to say, "this code will run on all exits"

@brson

This comment has been minimized.

Show comment
Hide comment
@brson

brson Mar 13, 2012

Contributor

I took another stab at it:

fn main() {
    let a = @"toast";
    {|finally| try(finally) {||
        log(error, "test");
    } } {||
        log(error, a);
    }
}

fn try(finally: fn(), try: fn()) {
    resource r(finally: fn@()) {
        finally();
    }
    unsafe {
        let finally: *u8 = unsafe::reinterpret_cast(ptr::addr_of(finally));
        let wrapper = fn@() {
            (*unsafe::reinterpret_cast::<*u8, *fn()>(finally))();
        };
        let _r = r(wrapper);
        try();
    }
}

It's hideous, but it's all stack closures.

Contributor

brson commented Mar 13, 2012

I took another stab at it:

fn main() {
    let a = @"toast";
    {|finally| try(finally) {||
        log(error, "test");
    } } {||
        log(error, a);
    }
}

fn try(finally: fn(), try: fn()) {
    resource r(finally: fn@()) {
        finally();
    }
    unsafe {
        let finally: *u8 = unsafe::reinterpret_cast(ptr::addr_of(finally));
        let wrapper = fn@() {
            (*unsafe::reinterpret_cast::<*u8, *fn()>(finally))();
        };
        let _r = r(wrapper);
        try();
    }
}

It's hideous, but it's all stack closures.

@catamorphism

This comment has been minimized.

Show comment
Hide comment
@catamorphism

catamorphism Apr 12, 2012

Contributor

If resources are going away, to be replaced with classes, how does that affect this issue? Currently it doesn't seem possible to have "anonymous" classes, since classes are inherently nominal.

Contributor

catamorphism commented Apr 12, 2012

If resources are going away, to be replaced with classes, how does that affect this issue? Currently it doesn't seem possible to have "anonymous" classes, since classes are inherently nominal.

@ghost ghost assigned brson Apr 12, 2012

@brson

This comment has been minimized.

Show comment
Hide comment
@brson

brson Apr 14, 2012

Contributor

I'm hoping that classes with region fields can be used to build defer, but I don't know how.

Contributor

brson commented Apr 14, 2012

I'm hoping that classes with region fields can be used to build defer, but I don't know how.

@catamorphism

This comment has been minimized.

Show comment
Hide comment
@catamorphism

catamorphism Feb 7, 2013

Contributor

Closing this because the last activity was 10 months ago and I'm going to assume that was @brson was hoping for is true. Reopen if necessary.

Contributor

catamorphism commented Feb 7, 2013

Closing this because the last activity was 10 months ago and I'm going to assume that was @brson was hoping for is true. Reopen if necessary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment