www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - A request: templated opCast() signature for class/struct

reply Pragma <ericanderton yahoo.removeme.com> writes:
Walter, All,

   I know this has been bandied about before, but I think it warrants some
serious attention.  Heck, I even argued 
against it before, until I saw what was really needed (My apologies, Sean).

The problem, for the uninitiated, is that D cannot overload by return type. 
This leaves exactly one possible signature 
for any given struct or class, should one need to implement opCast().  This
yields the following problems:

- Can only cast() to one explicit type other than the given type of the
enclosing struct/class
- D does not recognize a templated opCast() signature as mapped to implicit or
explicit casts
- Behaves non-uniformly when compared to other operator overloads (we can
template other operators just fine)
- Doesn't fit to most use-cases: you rarely want to map to be able to cast to
just one other type
- Is a blocker for implementing anything resembling duck typing.


I'd like to propose the following behaviors for the use of an templated
opCast() signature:

- Map "T opCast(T)()" to the same class of operations and situations as "T
opCast()"
- Still allow the traditional "T opCast()" signature.


This comes along with the following ideas and implications that I'm not sure of:

- Does opCast() even map to implicit casts at all?  Maybe it should in some
cases?
- DMD could map T opCast(T)() to implicit cast() for non-scalar types (can this
mess with inheritance?)


As far as making this jive with existing code, I would expect DMD to simply try
the explicit signature with T as the 
return type, and failing that, try matching the templated signature.  This
would all be in place of the current opCast() 
method match check in the semantic pass.

AFAIK, DMD will not let you overload templates by return type anyway, so the
programmer is left to pick one opCast() 
style over the other.  That is to say, one cannot have "bool opCast()" and "T
opCast(T)()" in the same class/struct at 
present.  So this presents no additional obstacles for developers, aside from
the meta-programming chops needed to 
support multiple results for casting.

(The current motivation, aside from countless other posts, for this idea is
here: 
http://www.dsource.org/projects/tango/forums/topic/12)

-- 
- EricAnderton at yahoo
Feb 16 2007
parent reply Walter Bright <newshound digitalmars.com> writes:
Pragma wrote:
 The problem, for the uninitiated, is that D cannot overload by return 
 type.

I think I understand the problem, and what you're trying to do. The problem is that T opCast(T)() wouldn't operate like anything else in D, it would be a total hack. I don't know what the right answer is here, we'll just have to work on it. There are currently no user defined implicit casts, though Andrei has suggested adding them with something like opImplicitCast().
Feb 17 2007
next sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Walter Bright wrote:
 Pragma wrote:
 The problem, for the uninitiated, is that D cannot overload by return 
 type.

I think I understand the problem, and what you're trying to do. The problem is that T opCast(T)() wouldn't operate like anything else in D, it would be a total hack. I don't know what the right answer is here, we'll just have to work on it. There are currently no user defined implicit casts, though Andrei has suggested adding them with something like opImplicitCast().

Probably the signature for opCast should be: void opCast(out T destination); which fortunately brings extra motivation to keep "out" in the language :o). "We're all a big family," inout said. That wold allow defining multiple _explicit_ casts. opImplicitCast should follow a similar signature, or probably the more flexible global: void opImplicitCast(T from, out U to); and follow the same exact rules are built-in conversions. In other words, in the graph I sent a while ago, the user would be able to put their own types and draw new arrows that add to the graph, indistinguishable from the current arrows. New arrows among built-in types would be possible, but otherwise any arrows having a user-defined type on one end should be allowed. The problem with implicit casts is that they fight directly against modularity. I don't know how to make the global signature modular; the member signature is easier to tame. Andrei
Feb 17 2007
next sibling parent kris <foo bar.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:

 which fortunately brings extra motivation to keep "out" in the language 
 :o). "We're all a big family," inout said.

There were two skunks who lived in the same burrow -- one named "out" and the other named "in". When in was out, out was in. And when in was in, out was out. How did out know when in was in? in-stinct
Feb 17 2007
prev sibling parent reply Kevin Bealer <kevinbealer gmail.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Walter Bright wrote:
 Pragma wrote:
 The problem, for the uninitiated, is that D cannot overload by return 
 type.

I think I understand the problem, and what you're trying to do. The problem is that T opCast(T)() wouldn't operate like anything else in D, it would be a total hack. I don't know what the right answer is here, we'll just have to work on it. There are currently no user defined implicit casts, though Andrei has suggested adding them with something like opImplicitCast().

Probably the signature for opCast should be: void opCast(out T destination); which fortunately brings extra motivation to keep "out" in the language :o). "We're all a big family," inout said. That wold allow defining multiple _explicit_ casts. opImplicitCast should follow a similar signature, or probably the more flexible global: void opImplicitCast(T from, out U to); and follow the same exact rules are built-in conversions. In other words, in the graph I sent a while ago, the user would be able to put their own types and draw new arrows that add to the graph, indistinguishable from the current arrows. New arrows among built-in types would be possible, but otherwise any arrows having a user-defined type on one end should be allowed. The problem with implicit casts is that they fight directly against modularity. I don't know how to make the global signature modular; the member signature is easier to tame. Andrei

Maybe the current module (where the cast happens) could be checked for a supported cast; then the module of the 'from' type, then the module of the 'to' type*. This would at least bound the locations where the definition could go. It allows multi-module semantics, but disallows global semantics. The modularity problem is lessened a bit. In a sense, it's like a member signature in that the author of either object is involved in writing the cast operation. There is also some precedent for this kind of 'access' because all elements of a module can access the fields of the relevant class. * (My rationale for checking the 'from' type first is that your opCast signature is likely to use an exact 'to' type, but may define the 'from' type in generic terms. It's more important to avoid losing data at the source by accidentally picking signature matching a base class when a more specific version is available.) Kevin
Feb 18 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Kevin Bealer wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Walter Bright wrote:
 Pragma wrote:
 The problem, for the uninitiated, is that D cannot overload by 
 return type.

I think I understand the problem, and what you're trying to do. The problem is that T opCast(T)() wouldn't operate like anything else in D, it would be a total hack. I don't know what the right answer is here, we'll just have to work on it. There are currently no user defined implicit casts, though Andrei has suggested adding them with something like opImplicitCast().

Probably the signature for opCast should be: void opCast(out T destination); which fortunately brings extra motivation to keep "out" in the language :o). "We're all a big family," inout said. That wold allow defining multiple _explicit_ casts. opImplicitCast should follow a similar signature, or probably the more flexible global: void opImplicitCast(T from, out U to); and follow the same exact rules are built-in conversions. In other words, in the graph I sent a while ago, the user would be able to put their own types and draw new arrows that add to the graph, indistinguishable from the current arrows. New arrows among built-in types would be possible, but otherwise any arrows having a user-defined type on one end should be allowed. The problem with implicit casts is that they fight directly against modularity. I don't know how to make the global signature modular; the member signature is easier to tame. Andrei

Maybe the current module (where the cast happens) could be checked for a supported cast; then the module of the 'from' type, then the module of the 'to' type*. This would at least bound the locations where the definition could go. It allows multi-module semantics, but disallows global semantics. The modularity problem is lessened a bit.

This should work. Also because D creates symbol tables "in parallel" - all symbols in a module are defined conceptually simultaneously, benefit that I momentarily forgot about :o).
 In a sense, it's like a member signature in that the author of either 
 object is involved in writing the cast operation.  There is also some 
 precedent for this kind of 'access' because all elements of a module can 
 access the fields of the relevant class.
 
 * (My rationale for checking the 'from' type first is that your opCast 
 signature is likely to use an exact 'to' type, but may define the 'from' 
 type in generic terms.  It's more important to avoid losing data at the 
 source by accidentally picking signature matching a base class when a 
 more specific version is available.)

Makes sense. Andrei
Feb 18 2007
prev sibling parent pragma <ericanderton yahoo.com> writes:
Walter Bright wrote:
 Pragma wrote:
 The problem, for the uninitiated, is that D cannot overload by return 
 type.

I think I understand the problem, and what you're trying to do. The problem is that T opCast(T)() wouldn't operate like anything else in D, it would be a total hack. I don't know what the right answer is here, we'll just have to work on it.

Thanks for the reply. I understand completely - if there's a better way to implement multiple opCast() overloads, without introducing kludges, I'm all for it. :) After reading some of the replies here, it seems that you and Andrei have this ball rolling already. Frankly, I'm just glad that the opCast() issue, overall, is getting some attention.
 There are currently no user defined implicit casts, though Andrei has
 suggested adding them with something like opImplicitCast().

It's not a must, but it would certainly help with transparency in a bunch of places. Properly implemented, it would be quite a powerful feature.
Feb 18 2007