Potential religious war alert !!

Potential religious war alert !!

What do you think it's better?

procedure Foo(const Text: string); overload;
procedure Foo(const Text: string; const Args: array of const); overload;

vs

procedure Foo(const Text: string);
procedure FooFmt(const Text: string; const Args: array of const);

You may take into account performance, readability, religion, taste, and so on

Comments

  1. I prefer the overloading approach, mostly because of readability and less typing. Also because it "looks like" there are fewer methods and it gives the sensation of a more simpler interface. And last, because IDE Insight will show all the calling posibilities at a glance, so maybe I spot one method I'm interested and suddenly I receive more info about the class; I don't need to inspect if "would be there some method that let me pass in the format args or i need to call Format() myself?"

    ReplyDelete
  2. I want optional/named parameters so there only needs to be one procedure... sniff... sob.... weep.... :-(

    ReplyDelete
  3. In the past I would prefer the overloading approach, but now I'd argued that, well-defined names give better clarity and clearness. I mean, by looking at the names, especially in the case of code-completion list, I typed "myObj.Fo", if both the methods are named "Foo", I wouldn't know more details until I see it's method signatures, but if I see the "Fmt" postfix, I"ll immediately understand that that method supports string formatting. I guess such comparision is like comparing "statically-typped" languages such as Delphi, TypeScrypt, vs "dynamic languages" such as JavaScript and Python.

    ReplyDelete
  4. Separate methods because what the code is doing is immediately obvious without having to waste time looking up IDE insight information, and it also provides the purpose of the parameters.

    The Fmt suffix makes the purpose of the array obvious, without it the array could have any other purpose than use as Format() parameters.

    To make it obvious the Fmt suffix carries information the IDE cannot provide, if instead of "Foo" the method is "SQLQuery":
    - "SQLQueryFmt" you instantly know it's an sql injection vulnerability if there is any "%s" used.
    - "SQLQuery" the array parameters are not for a Format() but for an ordered binding of the sql query parameters.

    ReplyDelete
  5. Eric Grange

    Doesn't separate methods give users two procedures to remember rather than one? In languages with named parameters, this is always going to be one function with an optional parameter, isn't it?

    ReplyDelete
  6. Joseph Mitzen​ not really, they have the same root, so are suggested alongside in code suggestions. It just makes the alternatives less ambiguous in purpose.

    Now if it was FmtFoo vs Foo the issue you raise would be valid.

    ReplyDelete
  7. They both have their place. E.g. if foo sets a property to the value of it's parameter, it's nice to have overloaded versions that take a number, a string or a record, as long as it is obvious what they so. More complex functions? I'm of two minds here. Having suffixes to distinguish between them just looks ugly. In particular appending an "ex" is far from a good naming convention. But for understanding the code (you spend more time maintaining/debugging it than writing it) it helps a lot if you don't have to guess or analyse which method is being called. If ctrl-click simply worked, that would be a great help.

    ReplyDelete
  8. In this scenario the standard library has laid down a string precedent for you. Use Foo and FooFmt as it does.

    ReplyDelete
  9. in any case Foo is definitely not a good name for a function :)

    procedure ShowMessage(const Msg: string);
    procedure ShowFormatedMessage(const Fmt: string; Args: array of const);

    note that even the parameter name is not the same

    ReplyDelete
  10. Paul TOTH​​ I was not asking how should I name some methods. Just curious about how and why other devs do things.

    Foo is good for meaningless code where the functionality is not important, but the design/approach.

    But of course nobody will have a library with a method called foo!

    I like the naming parameters different though

    ReplyDelete
  11. Agustin Ortu just kidding ;)

    but even with same parameters I prefer the OpenGL approch

    procedure glVertex2i(x, y: Integer);
    procedure glVertex2f(x, y: Single);
    procedure glVertex3i(x, y, z: Integer);
    procedure glVertex3f(x, y, z: Single);

    ReplyDelete
  12. Paul TOTH Yes! We should code for readability - at least readability should be put in the first place.

    ReplyDelete
  13. I miss a third "It depends" option.
    Same operation, different types - overloads.
    Similar operations, different key values - same prefix, context specific postfix.

    ReplyDelete
  14. Lars Fosdal​ as in Paul TOTH​ example of OpenGL, I would add that for an overload to be fine on the same operation, it has to be without side effects "that matter" - use your judgement :)

    OpenGL being performance oriented and precision sensitive, an extra cast can matter. But in other cases, casting automatically between integers and floats would not matter, so an overload would be just fine.

    ReplyDelete
  15. Lars Fosdal​ if "it depends" was an option I'd removed the others. Come on, it's programming. It's always 'it depends'. That would be no fun and it wouldn't generate any discussion :)

    ReplyDelete
  16. Still, there are some things I don't like when overloading:

    1. The overload keyword itself. On C you just write the different signatures and you are off to go
    2. Why the compiler wont accept this?

    // E2252 Method 'Bar' with identical parameters already exists
    TFoo = class
    public
    function Bar: Integer; overload;
    function Bar: string; overload;
    end;

    And this does indeed compile:

    TFoo = class
    public
    procedure Bar(out Result: Integer); overload;
    procedure Bar(out Result: string); overload;
    end;

    ReplyDelete
  17. Lars Fosdal What about sorting and reverse sorting? An optional parameter for "sort", like most other languages, or sort() and rsort() like PHP?

    ReplyDelete
  18. Joseph Mitzen​ Most of my sorting is covered by my generic lists. If I need a custom sort, I pass a custom comparator. Edit: Heh, turns out I actually have a reverse order property on the list.

    ReplyDelete
  19. Agustin Ortu​ Which Bar function should be used when passed to a Format statement? That would be impossible for the compiler to decide.

    ReplyDelete
  20. Lars Fosdal​ in that case it's ok, the compiler should throw an error. But my real point is that currently the compiler is banning all the cases, when a integer := Bar could be resolved, for instance

    ReplyDelete
  21. Agustin Ortu the cases in which it could be resolved usefully fall in the extreme corner cases category, and the cases in which it would be incorrectly accepted by the compiler are legion.

    Should the compiler try to resolve "Bar(Foo(Bar))" for instance or "Bar() + Bar()*Bar()" ? The compiler can use a full blown solver, and could find solution whenever there is one, no matter how complicated the puzzle...
    But is the human reading the code likely to understand what is going on?
    How likely are you to end up with highly ambiguous code like for "with" constructs? (ambiguous for humans, unambiguous for the compiler)

    In the end, code is for humans, whenever humans have doubts when looking at the code or need to use puzzle solving tools to understand, then the code has failed.

    ReplyDelete
  22. David Heffernan Yes, just like FormatFmt(). Oh, but wait.... ;)

    ReplyDelete

Post a Comment