Profile photo of marc

by

New Syntax for Extension Methods in Delphi Prism

February 17, 2010 in .NET, Elements, Mono, MonoDevelop, Oxygene, RemObjects, Visual Studio

Ever since Extension Methods have been introduced in .NET 3.5, we’ve been pondering on a proper syntax to allow developers to define their own. C# uses an awkward syntax where it uses the this keyword (C#’s equivalent of self) as name of the first parameter. But that felt wrong. For one, it’s a very C-like thing to do (having an obscure syntax that is not really discoverable or understandable, it ranks right up there with =0 for abstract methods in C++), for another, the fact that methods receive a self pointer as first parameter shoud be an implementation detail of the language (or runtime), not a syntax feature.

Another idea was to simply allow a new extension directive to be appended to a method declaration (similar to virtual or similar constructs). But that too felt wrong.

So what we did, for the time being, was to not provide any special syntax at all, and let people simply define extension methods “manually”, by applying the appropriate attributes. Given that extension methods are consumed (which we have always supported) much more often than defined, we figured this was a good enough compromise, for the time being.

Then, a couple months back, i was doing some language research, among with was watching the introduction video for Go, the new system language created by Google. While in general Go’s syntax felt, to me, like C/C++ beaten with the ugly stick (no offense), it introduced some nice concepts, and one thing that struck me was how easily Go allowed to declare new methods for a class.

This, i thought, was how extension methods should work like, in Prism. An extension method, ultimately, should not have to be part of some arbitrary class that has nothing to do with that method or with the class you are extending. Forcing extension methods into a class syntax is not proper Object Orientation – on the contrary, it’s an abuse of OO syntax. And why shouldn’t you be able to just go ahead and declare the method – without having to learn some awkward syntax or rules?

i decided you should, and after convincing Carlo to do the actual implementation, i am happy to let you know you that starting with the upcoming Spring release of Delphi Prism, you can now define extension methods by, well, just defining them.

Behold:

extension method Int32.Twice: Int32;
begin
  result := self*2;
end;

If we overlook the new extension keyword for now, this looks like a regular method on Int32 – which, essentially, it is. There is no need to define an awkward “self” parameter; you just specify the method name prefixed with the type – as you’ve done thousands of times when writing your own classes – and the compiler takes it from there.

Only the extension keyword indicates that Int32 is not really your own class, but that you’re extending a class defined somewhere else.

This new syntax should make it much more convenient and attractive to define new extension methods, when and where you need them – without all the overhead of a class declaration.

Of course this works on more complex types, as well; the following complete program prints the square of all numbers between 0 and 100 – not by squaring the numbers, but by squaring the sequence of numbers. (It also shows off two other new language features we haven’t talked about yet ;)

namespace ExtensionMethods;
 
interface
 
uses
  System.Collections.Generic,
  System.Linq;
 
extension method IEnumerable<Int32>.Squared: IEnumerable<Int32>;
 
implementation
 
extension method IEnumerable<Int32>.Squared: IEnumerable<Int32>;
begin
  result := for each value in self yield value*value;
end;
 
begin
  var values := for i: Int32 := 0 to 100 yield i;
 
  for each i in values.Squared do
    Console.WriteLine(i);
end.

Let me know what you think!

37 responses to New Syntax for Extension Methods in Delphi Prism

  1. I like it, it reads nicely.

    Any particular why you felt that appending the extension keyword after the declaration felt wrong? It strikes me that as we do that for virtual, it seems like a logical place to look for it too?

    • Jamie: i didn’t think the extension directive as such was wrong, but the whole concept of having to define the method in some (arbitrary) class. It also did not solve the problem of having to somehow declare the class you’re extending (ie keep the first “self” parameter, which felt just as wrong).

      it was one thing living with these twop inconsistencies when we didn’t have any special syntax support at all (ie you just used regular Prism syntax AND knowledge of how EMs work internally, to define em), it was another to officially sanctify that approach.

  2. Looks really nice!

    You’re using IEnumerable in your last example. Can I extend arrays or sequences using the Prism-syntax for them? Something like this?

    extension method (sequence of Integer).Squared: sequence of Integer;
    extension method (array of Integer).Squared: array of Integer;

    • IEnumerable is essentially the same as “sequence of Integer”. i’ll need to check with Carlo if we can support these two syntaxes. it’s a parser thing. (he’ll probably swear at me for even asking ;)

    • I know about IEnumerable, that’s why I added “array of” to my question ;-)

    • ;)

      Carlo tells me its on the list (but it doesn’t work yet, that syntax)

  3. I love it, simple and to the point.
    Prism rocks, now get it out in the hands of more people.

    B.t.w., besides raising prices, has Embarcadero done anything useful regarding Prism yet?

    • among other things, they’ve “got it out in the hands of more people.” ;)

  4. Looks clean and understandable.

    Mike

  5. Very sweet!
    It’s exactly how Pascal should work: Easy to understand and intuitive syntax. That allows you to understand your own code even if you didn’t work on it for a while.

    • I for one disagree strongly that this line:

      var values := for i: Int32 := 0 to 100 yield i;

      Is *anything* like the way Pascal should work. It’s not easy to understand and it’s not intuitive. It’s code written for efficient parsing by a machine and humans that think that a few seconds saved in the single instant of creating code == increased productivity and who don’t think about the added time it then costs to read and properly understand that line of code in the years of it’s life and many visits under maintenance that will follow.

    • Jolyon: what”s unintuitive about it (the construct in general, not necessarily my use of it here)? it’s the same syntax as a regular for loop…

    • Inline declaration of variables (x2) and a flow control statement returning an object.

      When reading the code you have to keep resetting expectations…

      OK, so declaring a variable here… hmmm, don’t know what value or even type of value it will contain though… ah, now we have a flow control statement (loop), oh and another variable declaration – this one doesn’t have a “var” precursor tho and this time we can see it’s type and hmmm, this flow control statement doesn’t appear to control flow at all but rather seems to create some object which ooooh, now I see … is what is then assigned to “values” (but I still can’t see quickly and easily what type it is simply from reading the code).

      The alternative approach more in keeping with the “a place for everything and everything in it’s place” aims of Pascal (hence my opinion that this isn’t “the way Pascal should work”) would have been admittedly more verbose and would not have appealed to those who like to create code in haste and lament their maintenance headaches at leisure.

    • Ah, so your objection is not to the “for expression” syntax, then, but to inline variable declarations?

      fair enough. it’s a matter of taste, i suppose, and Prism still supports classic “var” sections. but i must say i have come to much prefer inline declaration, because it keeps variables close to the place they are used, and thus makes code easier to read, then if all the variables had been declared in one big chunk at the top.

      but YMMV, of course. this is just my own personal opinion/experience.

    • Yes sorry for any confusion – I thought that was clear from the quoting of the specific line I was commenting on.

      w.r.t keeping variables close to the place they are used, I would point out that the use of “i” in the subsequent “for each i” is referencing a variable that is declared as part of a flow control block statement (the scope of which then leaks into any code that follows that block) and which is itself embedded within an assignment statement.

      When inspecting “for each i”, if left wondering “what is “i” and where is it declared you can’t check the “normal place” where variables declarations are found… you have to go hunting for it.

      In this case “i” could be assumed to be an Integer and the assumption would be correct.

      However, encouraging “assumption” is a recipe for disaster in software ime. Some people use “i” as a mnemonic for generic “iterator variable”, for example, not specifically “integer counter”.

      As I say… create in haste, lament maintenance headaches at (your users/customers/bosses) leisure.

    • we’ll have to agree to disagree. *any* syntax allows to write readable and unreadable code. it all depends on what the developer makes of it.

      me, personally, i find variables declared where they are introduced MUCH mor readable in pretty much every case i’ve seen. That’s not to say one can write entirely incomprehensible code, of course. But you can do so in classic Delphi, as well – the location of the var declaration is usually the least of your problems, when you do.

  6. why do you need the “method” keyword, why not just

    extension Int32.Twice: Int32;
    begin
    result := self*2;
    end;

    nice and simpler

    • because it’s still a method. and this isn’t C, so brevity is not the main priority. i think terminology is important, and you’re defining an “extension method”, not an “extension”. (what happens if support for extension properties is added later?)

  7. What about defining static extension methods? Apparently the CLR allows for it, but there is no way to declare them in C#. F# allows for them.

    • Was my comment that bad?!?!

    • Your comment wasn’t bad. I missed it when I originally approved it. I’ll need to look into how F# does static extension methods as I have no idea how it does that at the moment; however I must add that even if F# can define them, C# and Prism won’t be able to use them.

  8. Rarely do I agree that new language features make things uglier and harder to read, but

    var values := for i: Int32 := 0 to 100 yield i;

    DEFINITELY fits the bill.

    Btw, should the return type for Squared not just be int32, not ienumerable int32?

    • but it’s the same as a regular for loop – the only difference is that it has a value (it’s an expression, not a statement). why is that ugly? (or, why is it more ugly than regular Pascal for loop?)

      • I think you meant to ask “How is it more ugly?”

        Here’s my OP for loop:
        for I := 0 to 100 do MyIntList.Add(I);
        One assignment, no type decs, 3 keywords.

        Compared to DP’s:
        var values := for I: Int32 := 0 to yield i;
        Two assignments, one colon, one type dec, 4 keywords.

        Of course, DP is doing more, and doing it with brevity. But as you say above, brevity is what C optimizes for. And brief can be efficient while not being elegant. The sequence/IEnumerable is more elegant than a TIntList, but the new ‘if expression’ is only elegant for some uses, while inelegant for others. OTOH, OP is going to need to do stuff elsewhere to support the single line listed above, and that might actually make the OP harder to understand at a glance, if the Pascal coder didn’t implement it in the expected Pascal fashion, follow coding style rules, etc.

        But… the DP example looks ugly because there should NEVER be more than one assignment operator in a single line of Pascal code. The in-line type declaration is a sin whose importance is debated in other comments in this thread. But add the type declaration to the double assignment, and you have FUGLY Pascal.

        My ‘wet compiler’ is going to be working overtime when parsing that line. Pascal is the best because I can parse a screen of code almost as fast as I can hit the page down key.

        When I work with C/Java/Ruby developers, they are blown away that I can scan many a never-before-seen 1000 line Pascal unit in 30 seconds and report almost exactly what it’s doing. That’s because Object Pascal, written properly, is wired for our language-processing-specialized brain better than any other language I’ve experienced.

        DP is not.

        Having said all that, I enjoy Ruby and like seeing more of its qualities at least available in some flavour of Pascal, even while I prefer another flavour for most tasks. This DP feature is a tool I want to have in the toolbox when needed.

        Better looking than the competition? Hell, yeah. But still ugly to traditional Pascal sensibilities.

        Keep up the good work.

        • Ross,

          that line of regular for loop you quote might be *simpler* (by a tiny bit) tan the Prism one , but it also does something entirely different.

          this really is a straw man argument. it’s not like for loops in Delphi Prism are inherently more complex or difficult to understand. a for loop that does the sam as what yur OP code does will look exactly the same in Prism. But the for expression is a *different* language feature. it’s not the same thing, just looking weirder, as you make it sound.

          the same ting goes for the inline var. that’s another straw man argument. declaring the var inline or nt has *zero* to do with the for loop syntax. yet, this is the 10th (or so) comment here that states that the for expressions are crap because inline var declaration sucks. that last part may be a matter of taste (i personally find it makes the code MORE readable, not less), but it has noting to do with the quality of the for expressions.

          it’s a similar argument as saying “your for expressions suck, coz you should not use single character variable names!”.

    • Squared extends IEnumerable and it returns a new IEnumerable, with the squared values of the original. It does *not* return a singe Int32. (if that were the case, say if we implemented

      extension method IEnumerable<Int32>.Sum: Int32;

      ), we’d of course use that – but that’s not the intention of this method.

  9. I like it.

    I’m glad you waited to give it some thought rather than implementing something that didn’t really fit that well.

    So, when can we play with it?

    • it’s in the current field test drop. and will ship with the upcoming Spring release.

  10. The core idea is nice. but I see 2 problems:
    1) How is the extension class named? It is going to be public or wouldn’t be of much use.
    But how could I influence how that class’ name is going to look like? I mean, how can I make sure that this public class fits naturally into the rest of my API?

    2) Doesn’t that syntax choke the compile, if one were to use “sequence of T”, instead of the, IMHO, ugly/verbose “IEnumerable”?

    The following doesn’t look right (even if the compile would except it, which I doubt):

    extension method sequence of T.DoSomething;

    You waited so long, is it really necessary to do it this way? (Mutually exclusive clashes with other parts of the language)

    The modifier alternative could easily account for more Oxygene-ish type names like “nullable T”, “array of T”, “array of T” or even…

    method DoSomething;
    extends parallel sequence of nullable T;

    Cheers,
    Robert

    Just saw that Christian has pointed the type name part it already…

    • 1) “none of your concern” ;) the whole point is that, as far as you know, there is no such thing as a class. thats an implementation detail. it will be so ein-accessible name such as @@Extensions. There’s n need for this class to fot your public API, as its not a class that anyone should access, directly.

      2) “sequence of T” syntax will be supported as well, but it will need parenthesis. Although personally i think the IEnumerable syntax makes more sense in this case. a sequence of T is an abstract language concept, which happens to be backed by an IEnumerable. But what you’re extending, really, is the iEnumerable.

      as for “of” – if i had the power to go back in time 5 years, id probably use “of” as general generic s syntax. ie “List of T”, instead of List. iirc there were some objections to that back them, but none of which are relevant today. but unfortunately that ship has sailed.Maybe when we do “Oxygene, the Next Generation” ;P

    • wrt 1) “@@Extensions”
      Isn’t that the path that leads to something similar as what Delphi.Net did. Which let me run away screaming every time I referenced one of their ugly assemblies?
      Not saying that doing it it this way is on the same atrocity-level as D.Net was. Just saying that you might get negative hear-say:
      “Oxygene? Yeah, I know that. Once used a library with all those weird names in it.”

      Seriously, I don’t think I’d ever use any feature if it would create public artifacts like this. The current, manual, syntax would still be valid, right?

    • Not really. your Extension class is something that SHOULD not be visible and used by consumers of your assembly. making it a “regular” class, imho, is what really causes confusion.

      this is no different then, say, a @p_Name field backing your short-syntax property. the field’s name is an intera compiler detail, not something to consume.

      As for the old syntax: its not a syntax at all. you’re just declaring a class with an attribute. so of course you can still do that.

    • Robert: It’s in the __Extensions class (current build has it in __Global but that will change). No @@; you’ll be able to call them directly through __Extensions if you want to (from C# and Prism)

  11. I like doing something about that syntax a lot. And what you are proposing looks pretty good to ME on first blush. I was considering making an extension method recently and I decided to not even try because I knew the syntax was unexpected and I would have to study it a while to figure out what to do. With the new syntax, I would be able to remember that even at a much later time because it looks like what I would expect it to.

    FWIW I on the side of the guys that don’t like “var for” line of code, but whoever likes that can use it, I guess.

    But the “extension” syntax change was a good idea.

    • note, though, that the “var” and the “for” are not the same construct. its just how i used them. you might as well do

      var
        values: sequence of Int32;
      begin
        values := for i: Int32 := 0 to 100 yield i;
      
      • Time will tell, but I doubt I would ever use “for” as an expression. In my view, it mixes flow control with assignment. That sort of thing just gives the mind too many things to have to think about at once. I know, I know, it is a simple example. But I can somehow imagine someone coding a whole lot of nested “for” expressions to make a real mess of understanding what the code is really doing just so they could say they used fewer lines of code.

  12. Can I use the it on enum?