www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Free functions versus member functions

reply Walter Bright <newshound1 digitalmars.com> writes:
Continuing the discussion from the thread "questions on PhanTango 'merger'":

Lars Ivar Igesund wrote:
 It is not as if such functions are non-existant in Tango, so which exact
 functionality do you think is better expressed through free standing
 functions rather than objects? The answers of others shows that this
 usually is wanted for objects where you often need only one operation on
 the given object, even if others are available. This don't remove the 
fact
 that an object (class) equally often is a useful abstraction, and 
when that
 is established, free standing functions usually should be implemented as
 wrappers around each method on the object, rather than the object being
 implemented via free standing functions. This is why Tango looks as 
it does
 today; we have avoided wrappers of our own code if possible, because they
 degrade orthogonality of the API, and add more code to maintain. 
Whether we
 have been to strict in enforcing that stance, is an open question.
I've been stumped by this design issue before. Should functionality be done as a set of free functions, or as member functions? I remember going over this with Matthew Wilson, and he resolved it by implementing two parallel sets of interfaces: one free, the other member. I thought that doing both was a copout, but couldn't figure out which one was right. I eventually ran across this article by Scott Meyers http://www.ddj.com/cpp/184401197 which made a lot of sense. It gives some good guidelines to use to make such decisions, and backs it up with reasoning that I find compelling. Isn't it funny how we've completed the circle? We went from all free functions in C, to all member functions in C++, and now back to free functions? <g>
Oct 10 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Continuing the discussion from the thread "questions on PhanTango 
 'merger'":
 
 Lars Ivar Igesund wrote:
  > It is not as if such functions are non-existant in Tango, so which exact
  > functionality do you think is better expressed through free standing
  > functions rather than objects? The answers of others shows that this
  > usually is wanted for objects where you often need only one operation on
  > the given object, even if others are available. This don't remove the 
 fact
  > that an object (class) equally often is a useful abstraction, and 
 when that
  > is established, free standing functions usually should be implemented as
  > wrappers around each method on the object, rather than the object being
  > implemented via free standing functions. This is why Tango looks as 
 it does
  > today; we have avoided wrappers of our own code if possible, because 
 they
  > degrade orthogonality of the API, and add more code to maintain. 
 Whether we
  > have been to strict in enforcing that stance, is an open question.
 
 I've been stumped by this design issue before. Should functionality be 
 done as a set of free functions, or as member functions? I remember 
 going over this with Matthew Wilson, and he resolved it by implementing 
 two parallel sets of interfaces: one free, the other member. I thought 
 that doing both was a copout, but couldn't figure out which one was right.
 
 I eventually ran across this article by Scott Meyers 
 http://www.ddj.com/cpp/184401197
 which made a lot of sense. It gives some good guidelines to use to make 
 such decisions, and backs it up with reasoning that I find compelling.
 
 Isn't it funny how we've completed the circle? We went from all free 
 functions in C, to all member functions in C++, and now back to free 
 functions? <g>
From the fine article: """ Herb Sutter has explained that the "interface" to a class (roughly speaking, the functionality provided by the class) includes the non-member functions related to the class, and he's shown that the name lookup rules of C++ support this meaning of "interface" [7,8]. """ Do D's name lookup rules similarly support that meaning of "interface"? (I'm not sure what the Herb was talking about but I'm guessing he meant things like preferring the most-derived type for overloads and argument dependent lookup.) --bb
Oct 10 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
  From the fine article:
 """
 Herb Sutter has explained that the "interface" to a class (roughly 
 speaking, the functionality provided by the class) includes the 
 non-member functions related to the class, and he's shown that the name 
 lookup rules of C++ support this meaning of "interface" [7,8].
 """
 
 Do D's name lookup rules similarly support that meaning of "interface"?
 (I'm not sure what the Herb was talking about but I'm guessing he meant 
 things like preferring the most-derived type for overloads and argument 
 dependent lookup.)
I think Herb was talking about ADL. D doesn't have ADL, but the next update will include "overload sets" which, although very different, accomplish the same thing.
Oct 10 2007
parent reply "Kris" <foo bar.com> writes:
"Walter Bright" <newshound1 digitalmars.com>
 I think Herb was talking about ADL. D doesn't have ADL, but the next 
 update will include "overload sets" which, although very different, 
 accomplish the same thing.
Does D + ADL == ADD ? :p
Oct 10 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Kris wrote:
 "Walter Bright" <newshound1 digitalmars.com>
 I think Herb was talking about ADL. D doesn't have ADL, but the next 
 update will include "overload sets" which, although very different, 
 accomplish the same thing.
Does D + ADL == ADD ? :p
Nope. O.D.D.
Oct 10 2007
prev sibling next sibling parent janderson <askme me.com> writes:
Walter Bright wrote:
 Continuing the discussion from the thread "questions on PhanTango 
 'merger'":
 
 Lars Ivar Igesund wrote:
  > It is not as if such functions are non-existant in Tango, so which exact
  > functionality do you think is better expressed through free standing
  > functions rather than objects? The answers of others shows that this
  > usually is wanted for objects where you often need only one operation on
  > the given object, even if others are available. This don't remove the 
 fact
  > that an object (class) equally often is a useful abstraction, and 
 when that
  > is established, free standing functions usually should be implemented as
  > wrappers around each method on the object, rather than the object being
  > implemented via free standing functions. This is why Tango looks as 
 it does
  > today; we have avoided wrappers of our own code if possible, because 
 they
  > degrade orthogonality of the API, and add more code to maintain. 
 Whether we
  > have been to strict in enforcing that stance, is an open question.
 
 I've been stumped by this design issue before. Should functionality be 
 done as a set of free functions, or as member functions? I remember 
 going over this with Matthew Wilson, and he resolved it by implementing 
 two parallel sets of interfaces: one free, the other member. I thought 
 that doing both was a copout, but couldn't figure out which one was right.
 
 I eventually ran across this article by Scott Meyers 
 http://www.ddj.com/cpp/184401197
 which made a lot of sense. It gives some good guidelines to use to make 
 such decisions, and backs it up with reasoning that I find compelling.
 
 Isn't it funny how we've completed the circle? We went from all free 
 functions in C, to all member functions in C++, and now back to free 
 functions? <g>
I'm sure you Walter have read these but for anyone else: I like this interview with Bjarne Stroustrup. I think it explains it reasonably well. http://www.artima.com/intv/goldilocks3.html Also Hurb Sutters book "C++ Coding Standards" as a good explanation: http://www.gotw.ca/publications/c++cs.htm Its actually the official "coding style guide" where I work. -Joel
Oct 10 2007
prev sibling next sibling parent reply "Kris" <foo bar.com> writes:
Yes, that perspective resonates, though I tend to use the term 'client 
functions' to describe what Scott is calling non-member functions.

There's some specific interest here with regard to D:

1) the Widget factory example is less clear in D, due to that lack of an 
equivalent 'namespace' mechanism. This is why such methods tend to reside 
inside the respective class/struct in D. Yes, one could perhaps use "import 
as" instead, but that would tend to muddy usage further. I think this 
indicates a minor annoyance with the D namespace mechanism. One that's 
bothered me from time to time <g>

2) the section on "syntax issues" is something that D, on the other hand, is 
fairly adept at: the support for "flip that outside left-hand reference into 
the first argument instead", which people got excited about regarding 
arrays, does help to maintain syntactic symmetry. That's assuming you can 
avoid namespace collisions in the process. As I understand it, some of the 
changes in D v2 are intended to address that latter concern?

The interesting thing overall is this: what Scott describes has minimal 
bearing on the concerns within Tango, since we've mostly been attuned to the 
client/member issue from the beginning. Where we do currently deviate, it is 


Having said that, and assuming I correctly understand some of the comments 
made about Tango (over the last year), perhaps Tango just doesn't have 
sufficient 'client' functions?




"Walter Bright" <newshound1 digitalmars.com> wrote in message 
news:feju2r$1nsn$1 digitalmars.com...
 Continuing the discussion from the thread "questions on PhanTango 
 'merger'":

 Lars Ivar Igesund wrote:
 It is not as if such functions are non-existant in Tango, so which exact
 functionality do you think is better expressed through free standing
 functions rather than objects? The answers of others shows that this
 usually is wanted for objects where you often need only one operation on
 the given object, even if others are available. This don't remove the
fact
 that an object (class) equally often is a useful abstraction, and
when that
 is established, free standing functions usually should be implemented as
 wrappers around each method on the object, rather than the object being
 implemented via free standing functions. This is why Tango looks as
it does
 today; we have avoided wrappers of our own code if possible, because 
 they
 degrade orthogonality of the API, and add more code to maintain.
Whether we
 have been to strict in enforcing that stance, is an open question.
I've been stumped by this design issue before. Should functionality be done as a set of free functions, or as member functions? I remember going over this with Matthew Wilson, and he resolved it by implementing two parallel sets of interfaces: one free, the other member. I thought that doing both was a copout, but couldn't figure out which one was right. I eventually ran across this article by Scott Meyers http://www.ddj.com/cpp/184401197 which made a lot of sense. It gives some good guidelines to use to make such decisions, and backs it up with reasoning that I find compelling. Isn't it funny how we've completed the circle? We went from all free functions in C, to all member functions in C++, and now back to free functions? <g>
Oct 10 2007
parent reply Regan Heath <regan netmail.co.nz> writes:
Kris wrote:
 Yes, that perspective resonates, though I tend to use the term 'client 
 functions' to describe what Scott is calling non-member functions.
 
 There's some specific interest here with regard to D:
 
 1) the Widget factory example is less clear in D, due to that lack of an 
 equivalent 'namespace' mechanism. This is why such methods tend to reside 
 inside the respective class/struct in D. Yes, one could perhaps use "import 
 as" instead, but that would tend to muddy usage further. I think this 
 indicates a minor annoyance with the D namespace mechanism. One that's 
 bothered me from time to time <g>
What about D's modules or packages, aren't they the equivalent of a namespace? If not, what do they lack? Regan
Oct 11 2007
next sibling parent reply Chad J <gamerChad _spamIsBad_gmail.com> writes:
Regan Heath wrote:
 
 What about D's modules or packages, aren't they the equivalent of a 
 namespace?  If not, what do they lack?
 
 Regan
I'm not sure about namespaces, but one thing that putting stuff in structs/classes does is it forces users to state where the functions/data they are using comes from. It's forced FQN (Fully Qualified Name) syntax in a sense. It is nicer than just using module-based FQN because Stdout.formatln() is shorter than tango.io.Stdout.formatln() and still gets the point across. It would be nice to be able to write stdio.writefln(), which wouldn't lose much since it is very unlikely that there will be a series of imports like so: std.stdio; somelib.stdio; somelib.subpackage.stdio; In above such cases, well, too bad. Someone trying to figure out where stdio.writefln came from will just have to look it up. I may have just conflated two things unfortunately: compulsory FQN vs. voluntary FQN is one, and the other is the notion of a partially qualified name (PQN?). Currently D modules are voluntary FQN, while structs/classes act as compulsory PQN. Another thing that makes me tend to throw things into structs and classes is that private-is-visible. Consider this: //main.d import std.stdio; import primary; import secondary; void main() { writefln( generate() ); } // primary.d public char[] generate() { return "Hello from primary!"; } // secondary.d private char[] generate() { return "Hell from secondary!"; } Compiling the above code results in the following error: main.d(7): Error: primary.generate at primary.d(2) conflicts with secondary.generate at secondary.d(2) When I declare something private, I generally don't want it to affect anything in other modules, at all. Other modules do get affected in D, in the form of compiler errors, but a similar effect can be gained by just throwing things into structs or classes. This has been discussed before. I remember Walter liking it this way. It was to allow people to "poison" certain function overloads. It's a small thing, but annoying.
Oct 11 2007
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Chad J" <gamerChad _spamIsBad_gmail.com> wrote in message 
news:fel0ol$tnn$1 digitalmars.com...

 It would be nice to be able to write stdio.writefln(), which wouldn't lose 
 much since it is very unlikely that there will be a series of imports like 
 so:
 std.stdio;
 somelib.stdio;
 somelib.subpackage.stdio;
 In above such cases, well, too bad.  Someone trying to figure out where 
 stdio.writefln came from will just have to look it up.


 I may have just conflated two things unfortunately: compulsory FQN vs. 
 voluntary FQN is one, and the other is the notion of a partially qualified 
 name (PQN?).  Currently D modules are voluntary FQN, while structs/classes 
 act as compulsory PQN.
You might already know about this, but D already has a way to force the use of module-level FQNs and a way to rename modules when importing them: static import foo; // all members have to be accessed as foo.* import stdio1 = somelib.stdio; import stdio2 = somelib.subpackage.stdio; // Now you can access stdio1.* and stdio2.* The only somewhat annoyting part about this is that you have to do something _at import time_, something whith can't be enforced by the library that's being imported. I think it might be nicer if you could, at the very least, declare a module itself as static (static module foo; for the above example), and when you import it, it's automatically a static import.
Oct 11 2007
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Regan Heath wrote:
 Kris wrote:
 Yes, that perspective resonates, though I tend to use the term 'client 
 functions' to describe what Scott is calling non-member functions.

 There's some specific interest here with regard to D:

 1) the Widget factory example is less clear in D, due to that lack of 
 an equivalent 'namespace' mechanism. This is why such methods tend to 
 reside inside the respective class/struct in D. Yes, one could perhaps 
 use "import as" instead, but that would tend to muddy usage further. I 
 think this indicates a minor annoyance with the D namespace mechanism. 
 One that's bothered me from time to time <g>
What about D's modules or packages, aren't they the equivalent of a namespace? If not, what do they lack?
Two big differences with D's modules and C++ namespaces 1) A single C source file can have any number of (possibly nested) namespaces 2) A single namespace can be split across parts of lots of files The result is that C++ namespaces are usually used to represent bigger collections of functionality. Like "IO" or "Net". Not IO.Conduit.BufferedNosePicker They're used more for package-level scoping than module-level. I don't think I have ever seen C++ code that uses a 1-file per 1-namespace mapping. You certainly could do that with C++, but there is just no reason to. Another difference is that in C++ you have to *explicitly* say you want to bring members of a namespace into your current namespace with a "using" directive. In contrast, D's imports throw all the symbols into the local namespace by default. You have to do something extra to prevent it ("static import" instead of import). "Using" is also a nice tool for manipulating visibility of namespaces. D doesn't have anything quite like it. You can say at the function level for instance "using SomeNamespace" and then you can use things from that namespace unqualified, but just to the end of that function. There's also a renamed using -- something like "using SNS = SomeNamespace;" I think. --bb
Oct 11 2007
parent reply Jari-Matti =?ISO-8859-1?Q?M=E4kel=E4?= <jmjmak utu.fi.invalid> writes:
Bill Baxter wrote:

 Another difference is that in C++ you have to *explicitly* say you want
 to bring members of a namespace into your current namespace with a
 "using" directive.
 
 In contrast, D's imports throw all the symbols into the local namespace
 by default.  You have to do something extra to prevent it ("static
 import" instead of import).
I haven't been using C++ lately, but isn't 'using namespace' similar to D import - it imports all symbols from the namespace. Explicit syntax is nameSpace::symbol IIRC.
 "Using" is also a nice tool for manipulating visibility of namespaces.
 D doesn't have anything quite like it.   You can say at the function
 level for instance "using SomeNamespace" and then you can use things
 from that namespace unqualified, but just to the end of that function.
 There's also a renamed using -- something like "using SNS =
 SomeNamespace;" I think.
Well isn't this what has been proposed for D already, i.e. imports inside non-module level scopes.
Oct 11 2007
next sibling parent reply BCS <ao pathlink.com> writes:
Reply to Jari-Matti Mäkelä,

 Bill Baxter wrote:
 
 Another difference is that in C++ you have to *explicitly* say you
 want to bring members of a namespace into your current namespace with
 a "using" directive.
 
 In contrast, D's imports throw all the symbols into the local
 namespace by default.  You have to do something extra to prevent it
 ("static import" instead of import).
 
I haven't been using C++ lately, but isn't 'using namespace' similar to D import - it imports all symbols from the namespace. Explicit syntax is nameSpace::symbol IIRC.
As I understand it the difference is that in D you can't access somthing without an import. In C++ you can access it, but you need the FQN if you don't use using.
Oct 11 2007
parent Jari-Matti =?ISO-8859-1?Q?M=E4kel=E4?= <jmjmak utu.fi.invalid> writes:
BCS wrote:

 Reply to Jari-Matti Mäkelä,
 
 Bill Baxter wrote:
 
 Another difference is that in C++ you have to *explicitly* say you
 want to bring members of a namespace into your current namespace with
 a "using" directive.
 
 In contrast, D's imports throw all the symbols into the local
 namespace by default.  You have to do something extra to prevent it
 ("static import" instead of import).
 
I haven't been using C++ lately, but isn't 'using namespace' similar to D import - it imports all symbols from the namespace. Explicit syntax is nameSpace::symbol IIRC.
As I understand it the difference is that in D you can't access somthing without an import. In C++ you can access it, but you need the FQN if you don't use using.
Well, yes. But in C++ you still need #include the definition some way first. The only difference is C++ and D have different kinds of mappings between namespaces and files. I guess you could get similar behavior by also extending import to import subscopes inside a module.
Oct 11 2007
prev sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Jari-Matti Mäkelä wrote:
 Bill Baxter wrote:
 
 Another difference is that in C++ you have to *explicitly* say you want
 to bring members of a namespace into your current namespace with a
 "using" directive.

 In contrast, D's imports throw all the symbols into the local namespace
 by default.  You have to do something extra to prevent it ("static
 import" instead of import).
I haven't been using C++ lately, but isn't 'using namespace' similar to D import - it imports all symbols from the namespace. Explicit syntax is nameSpace::symbol IIRC.
 "Using" is also a nice tool for manipulating visibility of namespaces.
 D doesn't have anything quite like it.   You can say at the function
 level for instance "using SomeNamespace" and then you can use things
 from that namespace unqualified, but just to the end of that function.
 There's also a renamed using -- something like "using SNS =
 SomeNamespace;" I think.
Well isn't this what has been proposed for D already, i.e. imports inside non-module level scopes.
Maybe, assuming the imports could be repeats of already imported modules. But anyway, proposed and implemented are very different things. People have also proposed that D compilers should be able compile C++ code directly. --bb
Oct 11 2007
prev sibling next sibling parent reply Kevin Bealer <kevinbealer gmail.com> writes:
Walter Bright Wrote:

 Continuing the discussion from the thread "questions on PhanTango 'merger'":
 
 Lars Ivar Igesund wrote:
  > It is not as if such functions are non-existant in Tango, so which exact
  > functionality do you think is better expressed through free standing
  > functions rather than objects? The answers of others shows that this
  > usually is wanted for objects where you often need only one operation on
  > the given object, even if others are available. This don't remove the 
 fact
  > that an object (class) equally often is a useful abstraction, and 
 when that
  > is established, free standing functions usually should be implemented as
  > wrappers around each method on the object, rather than the object being
  > implemented via free standing functions. This is why Tango looks as 
 it does
  > today; we have avoided wrappers of our own code if possible, because they
  > degrade orthogonality of the API, and add more code to maintain. 
 Whether we
  > have been to strict in enforcing that stance, is an open question.
 
 I've been stumped by this design issue before. Should functionality be 
 done as a set of free functions, or as member functions? I remember 
 going over this with Matthew Wilson, and he resolved it by implementing 
 two parallel sets of interfaces: one free, the other member. I thought 
 that doing both was a copout, but couldn't figure out which one was right.
 
 I eventually ran across this article by Scott Meyers 
 http://www.ddj.com/cpp/184401197
 which made a lot of sense. It gives some good guidelines to use to make 
 such decisions, and backs it up with reasoning that I find compelling.
 
 Isn't it funny how we've completed the circle? We went from all free 
 functions in C, to all member functions in C++, and now back to free 
 functions? <g>
I like Meyer's point here. My view is that classes provide the following added value: they let you define a bunch of operations without defining how they specifically will work. The example I think of is what I believe are two mistakes in the C++ STL, the first is 'sort'. I think it "should" be a member function, as vector::sort and list::sort have to be done differently for efficiency reasons. They give us the efficiency by making list::sort a member and vector can be sorted with sort(v.begin(), v.end()). But I think that's a mistake, since you can't write a good template that calls x.sort() and expect it to do the best thing. The other mistake (I think) is in the other direction, which is the 'extra' members that classes like string and vector have that give them personalities. For example, methods like "push_back()" and "rfind()" could be implemented externally for both string and vector, and it would result in less complexity AND more capability. (Maybe they wanted something like Boyer-Moore for string's find() related code?) Kevin
Oct 10 2007
parent janderson <askme me.com> writes:
Kevin Bealer wrote:
 Walter Bright Wrote:
 I eventually ran across this article by Scott Meyers 
http://www.ddj.com/cpp/184401197
 which made a lot of sense. It gives some good guidelines to use to 
make such decisions, and backs it up with reasoning that I find compelling.
 Isn't it funny how we've completed the circle? We went from all free 
functions in C, to all member functions in C++, and now back to free functions? <g>
 I like Meyer's point here.  My view is that classes provide the
 following added value: they let you define a bunch of operations
 without defining how they specifically will work.

 The example I think of is what I believe are two mistakes in the C++
 STL, the first is 'sort'.  I think it "should" be a member function,
 as vector::sort and list::sort have to be done differently for
 efficiency reasons.
I've found sort as a non-member function quite useful when I needed to change the code from a std::vector to a standard C array. A nice thing about non-member functions is they can work with privative types too.

 They give us the efficiency by making list::sort a member and vector
 can be sorted with sort(v.begin(), v.end()).  But I think that's a
 mistake, since you can't write a good template that calls x.sort()
 and expect it to do the best thing.
I'm not sure what you what you are getting at here. I don't think a member function list::sort was a good idea.
 The other mistake (I think) is in the other direction, which is the
'extra' members that classes like string and vector have that give them personalities. For example, methods like "push_back()" and "rfind()" could be implemented externally for both string and vector, and it would result in less complexity AND more capability. (Maybe they wanted something like Boyer-Moore for string's find() related code?)
 Kevin
Perhaps these could be external however it would probably restrict optimizations and verifications that can be applied to these (without exposing more of the class then you need too). However it does seem odd to have find functions in 2 different places and my pet peeve with STL is that it has low discover-ability. -Joel
Oct 11 2007
prev sibling next sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Walter Bright wrote:
 Continuing the discussion from the thread "questions on PhanTango 
 'merger'":
 
 
 I've been stumped by this design issue before. Should functionality be 
 done as a set of free functions, or as member functions? I remember 
 going over this with Matthew Wilson, and he resolved it by implementing 
 two parallel sets of interfaces: one free, the other member. I thought 
 that doing both was a copout, but couldn't figure out which one was right.
 
 I eventually ran across this article by Scott Meyers 
 http://www.ddj.com/cpp/184401197
 which made a lot of sense. It gives some good guidelines to use to make 
 such decisions, and backs it up with reasoning that I find compelling.
 
 Isn't it funny how we've completed the circle? We went from all free 
 functions in C, to all member functions in C++, and now back to free 
 functions? <g>
I don't think I fully agree with some points of that article. He states that a function that can be implemented as a "non-friend non-member" function instead of a member function, should be implemented that way, since it would reduce encapsulation. Well, sorta. Just because a function is written as a member function doesn't mean we have to access the classes fields directly in that function, one can use the class's external API just as if the function was non-member, and thus have no decrease in encapsulation. Second, I think that many times it is useful to have as part of the class API certain utility functions (that are not part of the minimal set and as such could be implemented as free functions), simply because they would be commonly useful. He mentions this aspect in the end of the article, but he gives the impression that he thinks including such functions is only worthwhile in rare occasions, and that classes should always have the minimal set of members, or close to it. I on the other hand think that most functions that could be expected to be used often should be part of the API. For instance, consider the IPath class of Eclipse: http://help.eclipse.org/help32/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/api/org/eclipse/core/runtime/IPath.html It's a class that represents a path name: a collections of path segments plus a device id. I think that class API/interface is ideally defined in terms of number of member functions, but according to Meyers, the class should have a lot less member functions. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Oct 13 2007
parent =?ISO-8859-1?Q?R=e9my_Mou=ebza._?= <ray.jay.ay.moueza pleasedontspam.gmail.com> writes:
API building can sometimes lead to classes that have methods quite unrelated to
what the objects are intended for, just because we need some utility functions
to be used with them.  Traits (as in
http://www.iam.unibe.ch/~scg/Research/Traits/) have been suggested to address
this problem. 
From what I understood we could use mixin template class to add utility
behaviour to a class ,in D. This would allow us to keep a minimalistic set of
members in a class and create new ones with reusable units of utility
behaviour. This also leads to simpler inheritance hierarchies. 
Here is what it could look like: 
interface SomeConstraints {
  // define some methods/properties requires for the trait to work
}
class Behaviour ( T : SomeConstraints ): T {
    // a traits is a stateless set of reusable methods and should only have
methods. 
    void extendedOperation ();
}
class Basic : SomeConstraints {
   // methods ;
}
void main () {
    Basic polymorphicBasic = new Behaviour !( Basic )(  args, ... ); 
    polymorphicBasic.extendedOperation (); 
}

Currently constructors are not inherited (unless I am not up to date) and
adding behaviours using this kind of code would not be easy without default
constructors. 

Bruno Medeiros Wrote:

 Walter Bright wrote:
 Continuing the discussion from the thread "questions on PhanTango 
 'merger'":
 
 
 I've been stumped by this design issue before. Should functionality be 
 done as a set of free functions, or as member functions? I remember 
 going over this with Matthew Wilson, and he resolved it by implementing 
 two parallel sets of interfaces: one free, the other member. I thought 
 that doing both was a copout, but couldn't figure out which one was right.
 
 I eventually ran across this article by Scott Meyers 
 http://www.ddj.com/cpp/184401197
 which made a lot of sense. It gives some good guidelines to use to make 
 such decisions, and backs it up with reasoning that I find compelling.
 
 Isn't it funny how we've completed the circle? We went from all free 
 functions in C, to all member functions in C++, and now back to free 
 functions? <g>
I don't think I fully agree with some points of that article. He states that a function that can be implemented as a "non-friend non-member" function instead of a member function, should be implemented that way, since it would reduce encapsulation. Well, sorta. Just because a function is written as a member function doesn't mean we have to access the classes fields directly in that function, one can use the class's external API just as if the function was non-member, and thus have no decrease in encapsulation. Second, I think that many times it is useful to have as part of the class API certain utility functions (that are not part of the minimal set and as such could be implemented as free functions), simply because they would be commonly useful. He mentions this aspect in the end of the article, but he gives the impression that he thinks including such functions is only worthwhile in rare occasions, and that classes should always have the minimal set of members, or close to it. I on the other hand think that most functions that could be expected to be used often should be part of the API. For instance, consider the IPath class of Eclipse: http://help.eclipse.org/help32/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/api/org/eclipse/core/runtime/IPath.html It's a class that represents a path name: a collections of path segments plus a device id. I think that class API/interface is ideally defined in terms of number of member functions, but according to Meyers, the class should have a lot less member functions. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Oct 13 2007
prev sibling parent reply Kevin Bealer <kevinbealer gmail.com> writes:
janderson Wrote:

 Kevin Bealer wrote:
  > Walter Bright Wrote:
  >>
  >> I eventually ran across this article by Scott Meyers 
 http://www.ddj.com/cpp/184401197
  >> which made a lot of sense. It gives some good guidelines to use to 
 make such decisions, and backs it up with reasoning that I find compelling.
  >>
  >> Isn't it funny how we've completed the circle? We went from all free 
 functions in C, to all member functions in C++, and now back to free 
 functions? <g>
  >
  > I like Meyer's point here.  My view is that classes provide the
  > following added value: they let you define a bunch of operations
  > without defining how they specifically will work.
  >
  > The example I think of is what I believe are two mistakes in the C++
  > STL, the first is 'sort'.  I think it "should" be a member function,
  > as vector::sort and list::sort have to be done differently for
  > efficiency reasons.
 
 I've found sort as a non-member function quite useful when I needed to 
 change the code from a std::vector to a standard C array.  A nice thing 
 about non-member functions is they can work with privative types too.
Me too; now that I think about it there is also the nice aspect that you can sort part of an array/list/vector with it.
  > They give us the efficiency by making list::sort a member and vector
  > can be sorted with sort(v.begin(), v.end()).  But I think that's a
  > mistake, since you can't write a good template that calls x.sort()
  > and expect it to do the best thing.
 
 
 I'm not sure what you what you are getting at here.  I don't think a 
 member function list::sort was a good idea.
There is an advantage to it, in that non-member function list needs to copy values but member function list can just switch pointers. This is important for a list of strings if you don't want the strings to be moved, or if you want to keep an iterator to a particular value from before to after the move. I've never tested the difference in speed so I don't know how important this is, but I think sort() can sometimes take advantage of internal features of a class. That said, having both member and non-member seems okay to me (for this case). But speed depends on the type T (as in vector<T>) as well. For some types, list<T>::sort might be faster and for others, vector<T>::sort. I seem to recall testing set<T> versus vector<T>::sort to see which worked faster and finding that one worked faster if T was int, and the other if T was string. (Just one implementation of course.)
  > The other mistake (I think) is in the other direction, which is the
 'extra' members that classes like string and vector have that give them
 personalities. For example, methods like "push_back()" and "rfind()"
 could be implemented externally for both string and vector, and it would
 result in less complexity AND more capability. (Maybe they wanted
 something like Boyer-Moore for string's find() related code?)
  >
  > Kevin
  >
 
 Perhaps these could be external however it would probably restrict 
 optimizations and verifications that can be applied to these (without 
 exposing more of the class then you need too).  However it does seem odd 
 to have find functions in 2 different places and my pet peeve with STL 
 is that it has low discover-ability.
 
 -Joel
I guess what I'm thinking is that find(a.begin(), a.end(), value) could be written, and string::find(...) could also be written. If the most efficient find() is the external one, then string::find() could just use that. If not, it can use an different internal version. My thinking is that users wanting speed can say s.find(...) and users wanting flexibility could say find(x.begin(), x.end(), ...) where x is any type. Maybe there's a nicer way to do this (I guess people use template specializations?) Kevin
Oct 14 2007
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Kevin Bealer wrote:
 I guess what I'm thinking is that find(a.begin(), a.end(), value) could be
written, and string::find(...) could also be written.  If the most efficient
find() is the external one, then string::find() could just use that.  If not,
it can use an different internal version.
 
 My thinking is that users wanting speed can say s.find(...) and users wanting
flexibility could say find(x.begin(), x.end(), ...) where x is any type.  Maybe
there's a nicer way to do this (I guess people use template specializations?)
 
 Kevin
 
Yes, Walter calls it "uniform function call syntax" or somesuch. Basically: s.find(...) and find(s, ...) become equivalent. This means that you can have a global find functions like thus: size_t find(T,U)(T collection, T thing_to_find) { ... } size_t find(T)(ICollection collection, T thing_to_find) { ... } And optimised member functions for particular implementations struct CrazyCollection(T) { size_t find(T thing_to_find) { ... } } And they all get invoked the same way. The user no longer has to care where the function was written, just that it exists. -- Daniel
Oct 14 2007
parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 10/15/07, Daniel Keep <daniel.keep.lists gmail.com> wrote:
 Basically:

   s.find(...)

 and

   find(s, ...)

 become equivalent.
Almost equivalent? Correct me if I'm wrong, but the former is capable of polymorphism while the latter isn't? I think this uniform call syntax idea is /brilliant/, by the way. I'm all in favor it. Just being clear here.
Oct 14 2007
parent Daniel Keep <daniel.keep.lists gmail.com> writes:
Janice Caron wrote:
 On 10/15/07, Daniel Keep <daniel.keep.lists gmail.com> wrote:
 Basically:

   s.find(...)

 and

   find(s, ...)

 become equivalent.
Almost equivalent? Correct me if I'm wrong, but the former is capable of polymorphism while the latter isn't? I think this uniform call syntax idea is /brilliant/, by the way. I'm all in favor it. Just being clear here.
Equivalent from the point of view of the person using it. They're *obviously* not equivalent in how they're implemented, where they're kept, or how they're mangled. I just didn't want to do a Raymond Chen and write the long, boring disclaimer where I precisely qualify every thing I said that could potentially be misinterpreted. I really don't have the patience for that. :) -- Daniel
Oct 15 2007