Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

A similar pattern that blew my mind when I first saw it was ruby's scoped open().

instead of

  f = open "test.txt"
  print f.read
  f.close
You can write

  open "text.txt" do |f|
    print f.read
  end
The file is automatically closed at the end of the block, and it also handles exceptions and so on. I find it also lines up better semantically with how I think about opening handles to things.

I believe Python also added something similar via the 'with' syntax. Not sure about other languages.



Yeah, Python has "context managers" that let you do the same thing. Here's the Python equivalent:

    with open("text.txt") as inf:
        print(inf.read())
User defined classes that implement special methods __enter__() and __exit__() can be used as context managers. More info: http://www.python.org/dev/peps/pep-0343/

For just file output/input Scheme has the "with-output-to-file" and "with-input-from-file" macros which redirect output and input to a given file. I vaguely remember Common Lisp having a similar thing, but I can't remember the name off hand.


Common Lisp has with-open-file for reading and writing files. It works pretty much the same way as everything else in this thread, so I won't post example code, but you can see some here:

http://www.lispworks.com/documentation/HyperSpec/Body/m_w_op...


I immediately thought of Python's context managers when I read this. What the article appeared to describe, which shouldn't be lost on any programmer, is "do stuff then clean up after yourself".


I often use:

  for line in open("name.txt"):
      print line # Or do something else with it.


Me too when I'm lazy but we shouldn't; it happens to work fine on CPython but on implementations use garbage collection instead of reference counting (e.g. Jython) it leaks file handlers. The "with" statement is the right cross-platform way to handle resources.


I'd imagine it's an implementation bug. Once the iterator finishes, the file should be closed and the file handler freed.


Perhaps in this case - but how about a case where you partially iterate over the file, but then the file object goes out of scope? CPython would close (and free) that immediately, but other implementations probably wouldn't.


While editing the answer to masklinn comment, this case came to me. You both are, of course, right. There is no reliable way to close the file from the for iterator.

:-/


> I'd imagine it's an implementation bug.

No, it's a code bug.

> Once the iterator finishes, the file should be closed and the file handler freed.

There is no reason for that to happen, let alone for that to happen deterministically.


Indeed. In generational garbage collectors, the file object may remain until the collector decides to collect it. But isn't the file object also raising a StopIteration error? Shouldn't it at least close itself then?


You can always fp.seek(0) and iterate again. Iteration and file openness are separate concepts, and any API worth its salt (e.g. the python file api) keeps it that way.


If you've opened that file in the right mode, you can conceivably append to it once you reach its end. Even in read only mode, I imagine you may conceivably seek back for further reading?


That wouldn't be the idiomatic "for line in file" loop.


As far as iteration goes, there is no difference between

    for line in open(somepath):
        # stuff
and

    f = open(somepath)
    for line in f:
        # stuff
and the second case would be outright broken if the file just decided to close itself when it stops iterating (as others noted, the developer may now want to append new content, or use file.seek and fly around the file's content).

Not to mention a major issue: `break`. The iterator gets no mention of breaks and the iteration is not terminated at this point (as far as the iterator is concerned, that is) so adding a `break` to your iteration (to stop it early because you've found the data/line you needed in a linear search) would suddenly start leaking file handlers where those were collected beforehand... not good.


For that to work as it does in CPython, the file would have to know it has no name reference attached to it and that its scope is limited to the iteration and that it can close the file when the iteration ends. I'd love to have this idiom supported under other implementations - it's beautiful.


> For that to work as it does in CPython, the file would have to know it has no name reference attached to it and that its scope is limited to the iteration and that it can close the file when the iteration ends.

Which will never happen and really has nothing to do with iteration (or the file object itself)

> I'd love to have this idiom supported under other implementations - it's beautiful.

I disagree on its beauty, but beyond that I can assure you it's never going to be a property of python-the-language.


On the other hand, the Ruby equivalent is

def in_context(*args) setup_context yield if block_given? ensure teardown_context end

I guess you could build a special class, but why?


In languages in which you can count on the timely cleanup of objects this can be done with the destructor. In C++, for instance, file handles are typically closed when the file object goes out of scope. Moreover, if an object has a file as a member variable, the object's default destructor will dispose of the file.

IIRC:

    {
        ifstream file("text.txt");
        copy(istream_iterator<string>(file), istream_iterator<string>(),
             ostream_iterator<string>(cout, "\n"));
    }//file automatically closed here
and

    class ThingWithFile {
        ifstream file;
      public:
        ThingWithFile(const char *filename) : file(filename) {}
    };
    void func() {
        ThingWithFile thing("file.name"); //file is opened
    } //file is closed


Deterministic destruction is an excellent language feature. I really wish there were a garbage-collected language that separated destruction from deallocation, because the two are completely orthogonal.

When an object goes out of scope, the destructor should run immediately; then, when the garbage collector wants to actually deallocate destroyed objects, it can go right ahead. No idiocy like in C# with “using” blocks.


If it were possible to efficiently detect "when an object goes out of scope" (actually the question is when the object becomes unreachable) in the general case, then every language would do it. They'd immediately run the destructor and immediate collect the memory, because why not? But it's not possible to efficiently detect when an object becomes unreachable in a general way, or at the very least we don't know how. (I'm not certain if it's actually been proven impossible.)


There's automatic reference counting, which kind of works in some cases. But fails spectacularly in others - a simple doubly-linked list will defeat ARC. In essence, any reasonably complex program that relies on it will probably leak memory, unless the programmer has taken the time to find the spots where the ARC fails and takes care of them manually.

If a reference cycle happens then the only good way to detect that the objects involved have been orphaned is to walk the object graph. Doing that every time an object is deallocated would be horribly inefficient. Also, if you're doing it then there's really no need for the reference counting in the first place, since the object graph walk is perfectly capable of finding all the orphan objects on its own. Much better to just do it periodically, so you can get a whole swath of objects each time you walk the graph.

At which point we've come to to garbage collection, and discovered why that's what all the kids are doing these days.


Reference counting is also not free by itself (ignoring its cycle problems). There's overhead to all the incrementing and decrementing. It can add up in modern systems that toss around objects willy-nilly. Reference counting also adds a ton of additional synchronization points in a multi-threaded scenario.


Yup. I don't think it's any coincidence that the only really prominent place where ARC is being used is iOS.

It's a platform where reference counting was already in place, where you can have a reasonable hope that most the programs are too small and simple to take much of a hit on the overhead, and where memory leaks can't get too far out of hand because the OS reserves the right to kill processes whenever it wants.


> where you can have a reasonable hope that most the programs are too small and simple to take much of a hit on the overhead

What overhead? There's no runtime overhead to ARC compared to manual reference counting.


If I understand the distinction you draw between deallocation and destruction correctly, then in .NET a well-designed object that does not maintain unmanaged resources is effectively destroyed the moment the last reference to it goes out of scope. After all, it's disappeared from the world, never to appear again.

All the garbage collector does is comes through later and deallocates the memory it consumed.


Normally, the deallocation of the destroyed object has no visible effect, and you're not expected to do anything with object after it's destroyed.

So, in case of C++, object is destroyed when goes out of scope, but the physical act of deallocation may be deferred by the standard library as whatever 'smart memory manager' wishes.


To support this you'd need refcounting of each object on top of the gc, which would be a substantial overhead.


Technically you only need refcounting of objects with a destructor. For everything else, destruction is a no-op and you can just let the GC re-use the object's memory. And if the destructor is used pretty much just for side effects (closing files & sockets etc.), these will typically be rare & pose little overhead.

Would suck in a dynamically-typed language, though, because you have no idea whether a reference points to an object with or without a destructor, and so always have to pay the overhead of checking and possibly decrementing the refcount. I don't really see this working without a static type system.

BTW, you're often better off with explicitly-scoped destruction (eg. the Ruby & Python way) than refcounting, for the same reason that boost::shared_ptr can be tricky and Java programs can leak memory. If you don't have clear object ownership & lifetimes, it's very easy to stash a reference to an object in some longer-lived object (a cache, for example) and accidentally hold onto it way longer than anticipated.


> Would suck in a dynamically-typed language, though, because you have no idea whether a reference points to an object with or without a destructor, and so always have to pay the overhead of checking and possibly decrementing the refcount. I don't really see this working without a static type system.

It's worse than that. Any object that contains references to other destructible objects (including transitive references), must have an implicit destructor to decrement the refcounts of the destructible objects it points to. In fact, any object that holds a reference, directly or indirectly, to a generic "Object" class or equivalent must have an implicit destructor. This makes a huge part of the object graph implicitly destructible.


This is exactly what Python does, and while Python isn't the fastest language around, it's not that slow either, when compared to other dynamically typed, interpreted languages.


OTOH, refcounting is one of the main reasons why it's virtually impossible to eliminate the GIL, which is a showstopper for many multi-core usages of Python.


My understanding is that circular references wreak havoc with the concept of 'scope of a single object', and make implicit deterministic destruction unsafe if not impossible:

a = new A(); b = new B(); a.b = b; b.a = a;

Whichever you destroy first, the other destructor will have a reference to an unconstructed object.


To get around this you can distinguish between references that imply ownership and those that don't. In C++ pointers and references do not imply ownership, but values do.

It isn't ideal, but I think it's an impossible situation - I think predictable destruction is fundamentally at odds with automatic, transparent memory management when you push the ideas to their limits. The idea of automatic resource cleanup is tied closely to the idea of tight (recursive) resource ownership. Automatic memory management exists mostly to take care of the cases when object ownership and lifetime isn't clear.

Reference-counting smart pointers (used properly) can technically give you deterministic destruction, but it's kinda beside the point. We want our file handles and our connections closed as soon as we're done with them, not some time in the future when someone else's code cleans up its references to our old objects.


Which is where .NET hits a pretty good compromise, IMO. For things where deterministic cleanup really is important (file handles, connections, COM objects, etc.) there's IDisposable to provide for deterministic destruction of that stuff.

Folks do still complain that they can't deterministically destroy managed resources such as instances of String. I suspect those folks have missed the plot. That desire is indeed at odds with the memory manager; what is being requested could not (for the most part - the CLR's large object heap does complicate the story a bit) be provided without wholesale downgrading .NET to a less efficient collection algorithm.

It'd be much wiser to instead learn how to implement performance-critical routines that really would benefit from manual memory management in a language that's better suited to that kind of task, such as C. Whining that a garbage collected run-time uses garbage collection is just being silly, not entirely unlike complaining that your horse isn't very good at swimming.


Objects aren't deallocated in garbage collected languages. The memory they had allocated just doesn't get copied to the new heap -- which is a big difference.


You could argue that objects aren't deallocated in non-GC languages either, the memory they had allocated is just available for other objects to use. Which sounds kinda like "deallocated" to me, and yet it's the exact same thing as the allocation pointer in a stop & copy GC being free to overwrite them.


Generally in other languages the memory is immediately freed and available to be reallocated. This is not true in most garbage collection strategies.


Nor is it particularly desirable. One of the cornerstones of how a modern generational GC performs as well as it does is that blocks of objects are freed up all at once.

If they were deallocated in a piecemeal fashion then:

- Freeing up memory would take longer, because you can't just change a pointer back to the root of the generation.

- Allocating memory would take longer, because it involves searching for a big enough empty slot rather than just throwing the new object on top of the heap.

- More memory is wasted overall, because of heap fragmentation.


That depends on the GC implementation. Maybe you're saying that because most modern collectors tend to generational collection, and copy between generation heaps.

But copying is not required for GC. The trusty mark-and-sweep algorithm doesn't copy, for example.

See: http://en.wikipedia.org/wiki/Garbage_collection_%28computer_...


In fact, one can claim that mark-and-sweep is one of the few garbage collection algorithms that actually collect garbage.

Many others collect non-garbage. Because there typically is way less non-garbage than garbage, that is what makes them competitive with alloc/free.


That's an interesting point, thanks.


I totally wonder why do they not have it this way. I have posted the same question on various forums but have never heard any real answer.


Well, deterministic deallocation is difficult to combine with non-deterministic garbage collection.

Going out of scope is deterministic, but the object that has gone out of scope might still be referenced from somewhere if the reference has "escaped". Sometimes automatic escape analysis can work when references escape, but not always.

Would you like the object to have its destructor called even though it might be still be accessible? What would happen if the program subsequently tries to access the object?

Most languages are naturally uncomfortable with tidying up some resoure while it still might referenced. For this reason languages prefer to leave it up to the programmer to explicitly manage destruction themselves.

A perfect example is C#'s IDisposable interface. The language obviously "knows" that a disposable object needs cleaning up after it is used - that's what IDisposable means - but it cannot work out when this should happen. So it lets the programmer decide when that actually happens. If a programmer wants destruction based on scope then she should use the using statement. If she wants destruction based on some other condition then that is possible too. The language cannot make this decision.

I hope that answers your question.


The CLR also has different concepts for destruction and deallocation. Not sure what the using block and IDisposable have to do with that.


Yeah!

Objects in the stack are easy to use, logical, and orders of magnitude faster to instantiate than objects in the heap!

(when the underlying runtime uses malloc)


I believe c# has something similar too, "using"

  using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.None))
  {
      Byte[] info = new UTF8Encoding(true).GetBytes("This is some text in the file.");
      // Add some information to the file.
      fs.Write(info, 0, info.Length);
  }


I was about to put this down. Although, the C# world requires an IDisposable interface and implies that you're operating on a resource.

Ruby has similar concept when you open a file and pass in a block to operate on the File object.

I'm pretty sure the author is just excited about the language and failed to recognize that this pattern is everywhere in different programming languages.


The author is excited that functions have their inverses by default, I think. Analogous construct in Python would be somethoug like:

  with_under do_something() as f:
    ...
translated to

  f = do_something()
  ...
  f.undo_something()
With do_something automatically matched to undo_something.


You can do the same thing in C# that you can in Ruby

    new FileEx (SOME_PATH, () => ConsoleEx.ReadLine (), () => ConsoleEx.CurrentLine != "QUIT").WriteAll ();
is equivalent to

    using (var file = new File(SOME_PATH))
    {
        string line = null;

        try
        {
             while((line = Console.ReadLine) != "QUIT")
             {
                file.WriteLine(line);
             }
        }
        catch (Exception ex)
        {
            throw;
        }
        finally
        {
            sw.Close();
        }
    }


For a second I was intrigued and tried to find that FileEx class that I obviously missed to notice before.

It doesn't seem to be part of the framework though? Homegrown and _you_ expand it to something that is equivalent to the second code snippet?


Yes, it is homegrown[1]. I have quite a few little utilities that use (or abuse) iterators to get something like RAII in C# (I don't like relying on using/IDisposable and I especially don't like requiring it in my public types).

Another one that I use a lot [2]:

    SqlCommandEx sql = "select * from person";

    foreach (dynamic result in sql)
    {
        var full_name = result.first_name + " " + result.last_name;

        DateTime dob = result.dob;
         //do other stuff

    } //connection automatically disposed.
[1] Relevant source: http://pastebin.com/LwHD8MHc

[2] https://github.com/noblethrasher/Prelude/blob/master/SQL.cs


I'm going to assume I missed something, because from what I'm reading:

1. You're avoiding IDisposable for reasons you don't explain and I fail to see

2. You replace it with an idiom which is supposed to do the same thing (except abusing iterators instead of using tools dedicated to that job) and which I don't believe works, as your connection will not (as far as I can see) be disposed of if there's an error in the foreach block. And the code you linked does not seem to use this "pattern" anywhere


Avoiding using/IDisposable is taste thing; I'm not saying that anyone else should do it.

If called from the `foreach` statement, the code in a finally block of an iterator is guaranteed to execute even if the loop exits early due to a `break` or exception. The compiler will also generate a call to Dispose() as well.

Also, it's not really an abuse. When dealing with an unmanaged resource, you're almost always either pulling a stream of data out or pushing a stream of data in. That's precisely the use case of IEnumerable and its dual, IObservable (SqlCommandEx is an example of IEnumerable and FileEx is a loose example of IObservable).

P.S. Now there might be a thus far undiscovered bug in my SqlCommandEx code to which I linked (e.g. the compiler only generates calls to Dispose() on IEnumerator<T> which might not work for dynamic types, I haven't checked) but it's possible to do what I described in principle.


Yes, you can do this but I usually just end up using the helper functions i.e

      System.IO.File.AppendAllText(path, "This is some text in the file.");
Also, why are you writing text to binary file instead of just writing the text as a text file?


Java 7 has a implementation pattern built in for this now:

String readFirstLineFromFile(String path) throws IOException { try (BufferedReader br = new BufferedReader(new FileReader(path))) { return br.readLine(); } }

Anything that implements the AutoCloseable interface gets this behavior for free.


In Perl you can just use scope:

  {
    open my $fh, '<', "text.txt";
    print scalar <$fh>;
  }

  # $fh goes out of scope so file handle is closed


That's a bit different, Python without `with` does the same, but that's just property of garbage collector in this case.


Correct. To mimic it exactly is straightforward though:

  sub with_open_file {
      my ($filename, $code) = @_;
      open my $fh, '<', $filename or die $!;
      $code->($_) for <$fh>;
      close $fh;
  }

  with_open_file 'text.txt' => sub {
      my $f = shift;
      print $f;
  };

And to add to my original answer it's not just the effects of GC that are handy but also arbitrary scoping. Here is an example of what you can do with nested dynamic scoping:

    our $rake = "in shed";
    sub where_is_rake { say $rake }

    where_is_rake;  # => in shed

    {
        # take rake out of shed
        local $rake = "rake in hand";
        where_is_rake;  # => rake in hand
    
        {
            # pile up leaves
            local $rake = "using rake";
            where_is_rake;  # = using rake
        }
    
        where_is_rake;  # => rake in hand
    }

    where_is_rake;  # => in shed


C# and VB have the `using` statement;

    using(var db = OpenDbConnection())
    {
       // read from db, connection closed on next curly.
    }


In D, this is done with scope guard (or you can use destructors):

    f = open("test.txt");
    scope(exit) f.close();
    print(f.read());
The scope(exit) causes the following statement to be executed at the exit of the scope, regardless of how it leaves (return, goto, fall through, throw, etc.).

The advantage over try-finally is the 'finally' statement is associated with the open, rather than some arbitrary distance away.


> In D, this is done with scope guard

Which is nowhere near as good, as the closing operation has to be setup explicitly. Go has the same issue with `defer`.


The various tradeoffs between try-finally, RAII, and scope guard is discussed here:

http://www.d-programming-language.org/exception-safe.html

Scope guard has a number of distinct advantages.


Common Lisp has had with-open-file for a long time now. The great thing about with-open-file is that it ensures the file gets closed regardless of what the code in the scope does, using unwind-protect. This implies you can use the same method to create your own with-held-resource macros that work the same way.


Clojure has with-open, to the same effect. I don't think Scala has one natively but the Odersky book covers how to make one using a by-name parameter and an anonymous class.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: