views:

183

answers:

7
+1  Q: 

Ab-using languages

Some time ago I had to address a certain C# design problem when I was implementing a JavaScript code-generation framework. One of the solutions I came with was using the “using” keyword in a totally different (hackish, if you please) way. I used it as a syntax sugar (well, originally it is one anyway) for building hierarchical code structure. Something that looked like this:

CodeBuilder cb = new CodeBuilder();

using(cb.Function("foo"))
{
    // Generate some function code
    cb.Add(someStatement);
    cb.Add(someOtherStatement);

    using(cb.While(someCondition))
    {
     cb.Add(someLoopStatement);

     // Generate some more code
    }
}

It is working because the Function and the While methods return IDisposable object, that, upon dispose, tells the builder to close the current scope. Such thing can be helpful for any tree-like structure that need to be hard-codded.

Do you think such “hacks” are justified? Because you can say that in C++, for example, many of the features such as templates and operator overloading get over-abused and this behavior is encouraged by many (look at boost for example). On the other side, you can say that many modern languages discourage such abuse and give you specific, much more restricted features.

My example is, of course, somewhat esoteric, but real. So what do you think about the specific hack and of the whole issue? Have you encountered similar dilemmas? How much abuse can you tolerate?

+3  A: 

Offtopic, but just take a look at how pretty this becomes with lambdas:

var codeBuilder = new CodeBuilder();
codeBuilder.DefineFunction("Foo", x =>
{
    codeBuilder.While(condition, y =>
    {
    }
}
Anton Gogolev
nice, I have to look more into c# 3.0
Untrots
+3  A: 

I think this is something that has blown over from languages like Ruby that have much more extensive mechanisms to let you create languages within your language (google for "dsl" or "domain specific languages" if you want to know more). C# is less flexible in this respect.

I think creating DSL's in this way is a good thing. It makes for more readable code. Using blocks can be a useful part of a DSL in C#. In this case I think there are better alternatives. The use of using is this case strays a bit too far from its original purpose. This can confuse the reader. I like Anton Gogolev's solution better for example.

Mendelt
A: 

I wouldn't call it abuse. Looks more like a fancied up RAII technique to me. People have been using these for things like monitors.

heeen
+1  A: 

If you go by the strictest definitions of IDisposable then this is an abuse. It's meant to be used as a method for releasing native resources in a deterministic fashion by a managed object.

The use of IDisposable has evolved to essentially be used by "any object which should have a deterministic lifetime". I'm not saying this is write or wrong but that's how many API's and users are choosing to use IDisposable. Given that definition it's not an abuse.

JaredPar
+1  A: 

I wouldn't consider it terribly bad abuse, but I also wouldn't consider it good form because of the cognitive wall you're building for your maintenance developers. The using statement implies a certain class of lifetime management. This is fine in its usual uses and in slightly customized ones (like @heeen's reference to an RAII analogue), but those situations still keep the spirit of the using statement intact.

In your particular case, I might argue that a more functional approach like @Anton Gogolev's would be more in the spirit of the language as well as maintainable.

As to your primary question, I think each such hack must ultimately stand on its own merits as the "best" solution for a particular language in a particular situation. The definition of best is subjective, of course, but there are definitely times (especially when the external constraints of budgets and schedules are thrown into the mix) where a slightly more hackish approach is the only reasonable answer.

Greg D
+1  A: 

I often "abuse" using blocks. I think they provide a great way of defining scope. I have a whole series of objects that I use for capture and restoring state (e.g. of Combo boxes or the mouse pointer) during operations that may change the state. I also use them for creating and dropping database connections.

E.g.:

using(_cursorStack.ChangeCursor(System.Windows.Forms.Cursors.WaitCursor))
{
    ...
}
open-collar
+2  A: 

It would be better if the disposable object returned from cb.Function(name) was the object on which the statements should be added. That internally this function builder passed through the calls to private/internal functions on the CodeBuilder is fine, just that to public consumers the sequence is clear.

So long as the Dispose implementation would make the following code cause a runtime error.

CodeBuilder cb = new CodeBuilder();
var f = cb.Function("foo")
using(function)
{
    // Generate some function code
    f.Add(someStatement);
}
function.Add(something); // this should throw

Then the behaviour is intuitive and relatively reasonable and correct usage (below) encourages and prevents this happening

CodeBuilder cb = new CodeBuilder();
using(var function = cb.Function("foo"))
{
    // Generate some function code
    function.Add(someStatement);
}

I have to ask why you are using your own classes rather than the provided CodeDomProvider implementations though. (There are good reasons for this, notably that the current implementation lacks many of the c# 3.0 features) but since you don't mention it yourself...

Edit: I would second Anoton's suggest to use lamdas. The readability is much improved (and you have the option of allowing Expression Trees

ShuggyCoUk