January 07, 2004
what bether thing do you suggest then? with the operators, we get a nice simple syntax to the way it has to be: for every type-part of the stream, we have an "addObjToStream" function that gets called, and does exactly the needed work.

D is compile time typed, as is c++. so everything about the type determination should be compile time. only the needed part, a.k.a. the actual conversion of a type to the streamformat, should be runtime (if needed:D). and a direct function per type (and streamtype) would work perfect. not any more runtime overhead than needed.

but

write(stream,"Hello, my name is ");
write(stream,name);

is clumpsy (while exactly doable currently that way).

and that is why operator overloading would fit in that perfectly. it makes it possible to chain a lot of such write calls into one rather easy to read line.

if you can think logically, you would not try to judge phylosophically about it.

c++ provides a solution that is typesave, very simple extendable, working with existing language features, is simple, and can be very fast. the c++ version possibly isn't. but the D version can.

In article <btfjb9$2a4k$1@digitaldaemon.com>, Walter says...
>
>
>"Carlos Santander B." <carlos8294@msn.com> wrote in message news:btfh6d$26rc$1@digitaldaemon.com...
>> "Walter" <walter@digitalmars.com> wrote in message
>> news:btf3no$1hk9$2@digitaldaemon.com...
>> |
>> |
>> | Of course. But how member operator lookup works is:
>> |
>> |     a + b
>> |
>> | If a is an instance of Foo, then Foo.opAdd(b) is looked for. Foo can be
>in
>> | another module. No special ADL rule is required.
>> |
>> |
>>
>> What if all operators can be reversed? That way, if there's no
>> Foo.opAdd(typeof(b)), the compiler will look for typeof(b).opAdd_r(Foo).
>
>Since add is commutative, the compiler will first look for typeof(a).opAdd,
>if that isn't found, it will look for typeof(b).opAdd.
>
>> Also, those who want to possibly extend a stream class, would do MyType.opAdd_r(Stream). What about that?
>
>I philosophically object to using operator overloading for stream I/O <g>.
>
>


January 07, 2004
this is great. for OWN TYPES.

i'm still not able to add streaming capabilities to ANY type. types of libraries, types of a coworker, etc. you can't add streaming capability to some type without eighter change the stream class (just stupid), or the type class itself (just as stupid if its not your type).

streaming capability (as any operators actually) physically, and phylosophically, don't belong to a type. they are an algorithm, working on 2 types, wich they don't belong to. they just use them. that is basic oo, that is basic encapsulation. you can read a lot about it on cuj.com if you want.

don't make it a memberfunction if you don't need to. that is first rule of real oo and encapsulation.

In article <btfh6d$26rc$1@digitaldaemon.com>, Carlos Santander B. says...
>
>"Walter" <walter@digitalmars.com> wrote in message
>news:btf3no$1hk9$2@digitaldaemon.com...
>|
>|
>| Of course. But how member operator lookup works is:
>|
>|     a + b
>|
>| If a is an instance of Foo, then Foo.opAdd(b) is looked for. Foo can be in
>| another module. No special ADL rule is required.
>|
>|
>
>What if all operators can be reversed? That way, if there's no
>Foo.opAdd(typeof(b)), the compiler will look for typeof(b).opAdd_r(Foo).
>Also, those who want to possibly extend a stream class, would do
>MyType.opAdd_r(Stream). What about that?
>
>-----------------------
>Carlos Santander Bernal
>
>


January 07, 2004
"davepermen" <davepermen_member@pathlink.com> wrote in message news:btgjga$skd$1@digitaldaemon.com...
> what bether thing do you suggest then? with the operators, we get a nice
simple
> syntax to the way it has to be: for every type-part of the stream, we have
an
> "addObjToStream" function that gets called, and does exactly the needed
work.

There was an earlier thread about overloading () to implement stream I/O, it
works nicely and does not require ADL. It winds up looking like:
    stdout(arg1)(arg2)(arg3)
instead of (in C++):
    stdout << arg1 << arg2 << arg3
and besides, ADL isn't necessary for the latter either if << is overloaded
as a member of stdout.


January 07, 2004
It seems that operator overloads should belong at the module level since they
are really just syntactic sugar for functions.
The module would define and "export" the types then the module would define and
"export" the allowed operators on those types. Since D has strong typedefs this
shouldn't be too much of a problem.
This should also allow operator overload mixtures of standard primitive types
with module defined types. If you need to add more types and operators then the
module needs changing, since you have changed the specification of that module.
Of course, the module should be cohesive but that is up to the developer.

See Ada for a language that has been doing this for a long time: http://www.grammatech.com/rm95html-1.0/rm9x-06-06.html http://www.adaic.com/docs/95style/html/sec_5/5-7-4.html



In article <btbq8o$2g9e$1@digitaldaemon.com>, davepermen says...
>
>Walter
>
>this is essencial. not only for streaming libraries, but simply in general.. an
>
>Tc = Ta + Tb doesn't have ANY preference for any of those 3 types. it should be
>
>Tc opAdd(Ta a,Tb b) {}
>
>and not
>
>Tc Ta.opAdd(Tb b) {}
>
>or
>
>Tc Tb.opAddR(Ta a) {}
>
>or even worse, both!
>
>just get rid of it. binary operators don't belong to a member. they never did.
>
>and if you release that essencial missconcept, and replace it by the only real one, we gain immediate access to a very fast implementable typesave, and extendable streaming library..
>
>Tc operator add(Ta a,Tb b) { }
>
>would be my prefered way.
>
>


January 07, 2004
Hi,

Using binary operators like '+', '*', etc. in mathematical expressions is elegant and desirable. But, for some algebras the multiplicative operation is not commutative.

Is there a way to model alternative algebras (like geometric algebra) in
D and still be able to make use of the syntactic sugar the operators provide?

In particar, it would be desirable to turn of the commutative property of the '*' operator.

Kind regards,
Erik




In article <btfjb9$2a4k$1@digitaldaemon.com>, Walter says...
>
>
>"Carlos Santander B." <carlos8294@msn.com> wrote in message news:btfh6d$26rc$1@digitaldaemon.com...
>> "Walter" <walter@digitalmars.com> wrote in message
>> news:btf3no$1hk9$2@digitaldaemon.com...
>> |
>> |
>> | Of course. But how member operator lookup works is:
>> |
>> |     a + b
>> |
>> | If a is an instance of Foo, then Foo.opAdd(b) is looked for. Foo can be
>in
>> | another module. No special ADL rule is required.
>> |
>> |
>>
>> What if all operators can be reversed? That way, if there's no
>> Foo.opAdd(typeof(b)), the compiler will look for typeof(b).opAdd_r(Foo).
>
>Since add is commutative, the compiler will first look for typeof(a).opAdd,
>if that isn't found, it will look for typeof(b).opAdd.
>
>> Also, those who want to possibly extend a stream class, would do MyType.opAdd_r(Stream). What about that?
>
>I philosophically object to using operator overloading for stream I/O <g>.
>
>


January 07, 2004
And as well, the additive operation need not be commutative if you consider '+' to denote the binary operator of a monoid (for which the set of Java Strings with '+' as binary operator would be an example; http://mathworld.wolfram.com/Monoid.html)

The commutativity of + and * is in the eye of the beholder.

(By the way, while * in linear algebra is not commutative it is
associative -- (A * B ) * C == A * (B * C) -- so maybe it would be best
denoted by the behavior of the concatenation operator '~'! -- Just
kidding.)

-Antti

In article <bthcg2$21eo$1@digitaldaemon.com>, Erik Baklund wrote:
> 
> Hi,
> 
> Using binary operators like '+', '*', etc. in mathematical expressions is elegant and desirable. But, for some algebras the multiplicative operation is not commutative.
> 
> Is there a way to model alternative algebras (like geometric algebra) in
> D and still be able to make use of the syntactic sugar the operators provide?
> 
> In particar, it would be desirable to turn of the commutative property of the '*' operator.
> 
> Kind regards,
> Erik
> 
> 
> 
> 
> In article <btfjb9$2a4k$1@digitaldaemon.com>, Walter says...
>>
>>
>>"Carlos Santander B." <carlos8294@msn.com> wrote in message news:btfh6d$26rc$1@digitaldaemon.com...
>>> "Walter" <walter@digitalmars.com> wrote in message
>>> news:btf3no$1hk9$2@digitaldaemon.com...
>>> |
>>> |
>>> | Of course. But how member operator lookup works is:
>>> |
>>> |     a + b
>>> |
>>> | If a is an instance of Foo, then Foo.opAdd(b) is looked for. Foo can be
>>in
>>> | another module. No special ADL rule is required.
>>> |
>>> |
>>>
>>> What if all operators can be reversed? That way, if there's no
>>> Foo.opAdd(typeof(b)), the compiler will look for typeof(b).opAdd_r(Foo).
>>
>>Since add is commutative, the compiler will first look for typeof(a).opAdd,
>>if that isn't found, it will look for typeof(b).opAdd.
>>
>>> Also, those who want to possibly extend a stream class, would do MyType.opAdd_r(Stream). What about that?
>>
>>I philosophically object to using operator overloading for stream I/O <g>.
>>
>>
> 
> 
January 07, 2004
Walter wrote:
>>>The problem is if opAdd() is defined in another module that is not the
>>>current module. It will not be found with the normal scoped lookup. With
>>>ordinary functions, one just does:
>>>
>>>    foo.opAdd(a,b)
>>>
>>>if the function is in module foo. That doesn't work out so good for
> 
> operator
> 
>>>overloads. What ADL does is look at the types of the arguments to the
>>>function, and uses those types to add more scopes to look for the opAdd
> 
> in.
> 
>>Would that even be sufficient? Doesn't one expect to be able to write an
>>opAdd that works on objects whose classes are defined in other modules?
> 
> 
> Of course. But how member operator lookup works is:
> 
>     a + b
> 
> If a is an instance of Foo, then Foo.opAdd(b) is looked for. Foo can be in
> another module. No special ADL rule is required.

Yes, I understand that. But I still don't see how this issue is different from having "normal" overloaded global functions.

If I have two modules, foo and bar, each with a different version of function f, and I call f in another module, isn't that the exact same problem, as if f was an opAdd overload and the call an expression of the kind (a+b)?

I really don't see how global operators are different from global functions!

Hauke
January 07, 2004
davepermen wrote:
> what bether thing do you suggest then? with the operators, we get a nice simple
> syntax to the way it has to be: for every type-part of the stream, we have an
> "addObjToStream" function that gets called, and does exactly the needed work.
> 
> D is compile time typed, as is c++. so everything about the type determination
> should be compile time. only the needed part, a.k.a. the actual conversion of a
> type to the streamformat, should be runtime (if needed:D). and a direct function
> per type (and streamtype) would work perfect. not any more runtime overhead than
> needed.
> 
> but
> 
> write(stream,"Hello, my name is ");
> write(stream,name);
> 
> is clumpsy (while exactly doable currently that way).
> 
> and that is why operator overloading would fit in that perfectly. it makes it
> possible to chain a lot of such write calls into one rather easy to read line.
> 
> if you can think logically, you would not try to judge phylosophically about it.
> 
> c++ provides a solution that is typesave, very simple extendable, working with
> existing language features, is simple, and can be very fast. the c++ version
> possibly isn't. but the D version can.

It's an irk.  I don't see any way to work around it within the confines of D without resorting to polymorphism.  The question is merely whether polymorphism is a good way to deal with this.  Given that we're talking about something that would (certainly should) be in the standard library, I don't see why not.  Making streams deal with PDTs is trivial enough, since they are defined by the compiler, not the code.  Dealing with objects is similarly easy: define a Streamable interface, or just use Object.toString().

Structs require a few tiny hoops, but even that's pretty easy:

class StreamWrap(T) : IStreamable {
   this(T t) {
      _t = t;
   }

   void toStream(Stream stream) {
      stream.write(_t.toStream());
   }
}

The template could further be specialized for reference types and PDTs to provide genericity.

Free operator functions would be nice, but it doesn't seem to be the only good way to handle the streaming issue.

 -- andy
January 07, 2004
i do understand that. but you simply can't overload your prefered operator for ANY POSSIBLE TYPE EVER EXISTING. this is just not possible.

free overloadable functions give the ability to add overloads to your stream without any rewriting of stream or type.

THAT is why i want free operators. they are the way operators behave, and they are the only way to make streams fast extendable and type save. and FAST. everyone cries that they have to be faster than the c++ streams. believe me they can be hell fast. and i can for sure overload all sort of types for my stdout class and then write stdout << a << b << c; but this is nost scalable.

a library ALWAYS has to provide features a user can use for ANY situation. that means he has to have a nice plugin-interface. overloadable free operators are a GREAT plugin-interface for streams.

the by far best.

In article <btglpg$100p$1@digitaldaemon.com>, Walter says...
>
>
>"davepermen" <davepermen_member@pathlink.com> wrote in message news:btgjga$skd$1@digitaldaemon.com...
>> what bether thing do you suggest then? with the operators, we get a nice
>simple
>> syntax to the way it has to be: for every type-part of the stream, we have
>an
>> "addObjToStream" function that gets called, and does exactly the needed
>work.
>
>There was an earlier thread about overloading () to implement stream I/O, it works nicely and does not require ADL. It winds up looking like:
>    stdout(arg1)(arg2)(arg3)
>instead of (in C++):
>    stdout << arg1 << arg2 << arg3
>and besides, ADL isn't necessary for the latter either if << is overloaded as a member of stdout.
>
>


January 07, 2004
but the by far most straightforward and logical way. you don't need delegates as well, you can do that with a library-implementation. see boost::function.

the trick is, with operator overloading as free functions we get much more flexibility than only for streams. but there its most obvious. it will be as fast as directly setting up the specific code for your specific output stream format manually. (the chain of write(stream,T) calls), wich is the fastest way. in every other situation, we have to rely on compiler optimisations to _KNOW_ its as fast.

if you don't see that, you don't see encapsulation as something important, then? because operators don't belong to types, they should never be in their interface.

In article <bthkn2$2eq0$1@digitaldaemon.com>, Andy Friesen says...
>
>davepermen wrote:
>> what bether thing do you suggest then? with the operators, we get a nice simple syntax to the way it has to be: for every type-part of the stream, we have an "addObjToStream" function that gets called, and does exactly the needed work.
>> 
>> D is compile time typed, as is c++. so everything about the type determination should be compile time. only the needed part, a.k.a. the actual conversion of a type to the streamformat, should be runtime (if needed:D). and a direct function per type (and streamtype) would work perfect. not any more runtime overhead than needed.
>> 
>> but
>> 
>> write(stream,"Hello, my name is ");
>> write(stream,name);
>> 
>> is clumpsy (while exactly doable currently that way).
>> 
>> and that is why operator overloading would fit in that perfectly. it makes it possible to chain a lot of such write calls into one rather easy to read line.
>> 
>> if you can think logically, you would not try to judge phylosophically about it.
>> 
>> c++ provides a solution that is typesave, very simple extendable, working with existing language features, is simple, and can be very fast. the c++ version possibly isn't. but the D version can.
>
>It's an irk.  I don't see any way to work around it within the confines of D without resorting to polymorphism.  The question is merely whether polymorphism is a good way to deal with this.  Given that we're talking about something that would (certainly should) be in the standard library, I don't see why not.  Making streams deal with PDTs is trivial enough, since they are defined by the compiler, not the code.  Dealing with objects is similarly easy: define a Streamable interface, or just use Object.toString().
>
>Structs require a few tiny hoops, but even that's pretty easy:
>
>class StreamWrap(T) : IStreamable {
>    this(T t) {
>       _t = t;
>    }
>
>    void toStream(Stream stream) {
>       stream.write(_t.toStream());
>    }
>}
>
>The template could further be specialized for reference types and PDTs to provide genericity.
>
>Free operator functions would be nice, but it doesn't seem to be the only good way to handle the streaming issue.
>
>  -- andy