Hacker News new | ask | show | jobs
by kenneth_chau 5287 days ago
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.

2 comments

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.