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
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
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/sub
ReplyDelete/sub
ReplyDeleteI prefer overloading method, too.
ReplyDeleteI want optional/named parameters so there only needs to be one procedure... sniff... sob.... weep.... :-(
ReplyDeleteIn 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.
ReplyDeleteSeparate 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.
ReplyDeleteThe 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.
Eric Grange
ReplyDeleteDoesn'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?
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.
ReplyDeleteNow if it was FmtFoo vs Foo the issue you raise would be valid.
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.
ReplyDeleteIn this scenario the standard library has laid down a string precedent for you. Use Foo and FooFmt as it does.
ReplyDeletein any case Foo is definitely not a good name for a function :)
ReplyDeleteprocedure ShowMessage(const Msg: string);
procedure ShowFormatedMessage(const Fmt: string; Args: array of const);
note that even the parameter name is not the same
Paul TOTH I was not asking how should I name some methods. Just curious about how and why other devs do things.
ReplyDeleteFoo 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
Agustin Ortu just kidding ;)
ReplyDeletebut 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);
Paul TOTH Yes! We should code for readability - at least readability should be put in the first place.
ReplyDeleteI miss a third "It depends" option.
ReplyDeleteSame operation, different types - overloads.
Similar operations, different key values - same prefix, context specific postfix.
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 :)
ReplyDeleteOpenGL 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.
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 :)
ReplyDeleteStill, there are some things I don't like when overloading:
ReplyDelete1. 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;
Lars Fosdal What about sorting and reverse sorting? An optional parameter for "sort", like most other languages, or sort() and rsort() like PHP?
ReplyDeleteJoseph 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.
ReplyDeleteAgustin Ortu Which Bar function should be used when passed to a Format statement? That would be impossible for the compiler to decide.
ReplyDeleteLars 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
ReplyDeleteAgustin 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.
ReplyDeleteShould 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.
Good point
ReplyDeleteDavid Heffernan Yes, just like FormatFmt(). Oh, but wait.... ;)
ReplyDelete