July 29, 2004
"Sha Chancellor" <schancel@pacific.net> wrote in message news:schancel-C6DC99.10061329072004@digitalmars.com...
> Matthew,
>
>    Compiler bugs aside, you forté make a few silly arguments.  Such as,
> that you can't support iterators because you can't overload the *
> operator.  What kind of BS is that?

I said "Neither of these preclude the creation of iterators - and I've done a little of that - ".

Maybe you should have read it more thoroughly. :-(

> Overloading operators for no apparent good reason is STLs forté.  Obj-C and Cocoa have lots of templates and it doesn't have any operator overloading what-so-ever.
>
> STLs and C++ use operator overloading incessantly  because somehow their designers thinks it looks cool.

Wrong. (Or half wrong anyway). They overload the operators for conformance with pointers. Surely you know/recognise that a pointer is a RandomAccess iterator?

It's been my opinion - espoused often on the D ngs over the last year or so - that we don't need to directly support pointers as "first class" members of the enumerable club in D, since D's arrays (and any UDTs) are already enumerable via foreach().

This has been one of the motivating factors in my design toward a foreach-oriented library, with the consequences that two of the four of the enumeration "modes" / "models" in DTL are based on foreach.

>   IT confuses whether or not an iterator
> is a pointer or an object.  Why not just have a real function call:

Pointers, as explained above.

> Iterator.getMyFuckingInstanceNow()


Yes. The iterators I've played with have taken this approach.

> No instead we must confuse things by going (*Iterator)
>
> Iterators in STL suck, plain and simple.  Most of C++'s standard template library is overly complicated and obtuse for the job.  Not to mention buggy between implementations.

At the risk of pissing off YAA, I think this is naive. (Aside: pretty much everyone in the world apart from fellow Englishmen tend to get shirty if I mention the "n" word. For my part, it's much less offensive to have one's opinion called naive, than ill-informed, or half-considered, or plain dumb. So naive it is.)

Yes, C++'s library is complex, and it's going to get a lot more so in C++-0.x.

Yes, templates are hard to understand, and template implementations are virtually impossible not to become dialecticised (?). Consider, even though I value simplicity in templates more highly than pretty much any other aspect, I've done some reasonably complex things with STLSoft, and some people have commented that that stuff is pretty advanced/over-complicated/obtuse/cool/stupid. Point being, I understand the complex techniques I've used in STLSoft, but I cannot understand the implementation of even small Boost modules, for the same reason that some Boosters cannot understand STLSoft: C++ is too complex.

But, C++ is powerful, and that's why we stick with it. To stand on the sidelines and just shout about how hard it is is plain silly, until something replaces it. I don't think I speak out of turn to say that Walter would like that to be D. To achieve that D *must* support the currently unmatched power of C++. The other issues are (i) whether it becomes as hard, and (ii) whether it engenders the confusing and disenfranchising dialectisation (?) of C++. I my opinion we can live with (i) if we must, because most developers are reasonably intelligent. But (ii) will kill it, because learning limitless dialects is incredibly irritating and a waste of effort. If D ends up being as big a bag of wild-woman's knitting as C++, then what's the motivation to move to D?

[Apologies to any wild-women who have neat knitting.]

> As for implicit instantation,  Why is this required to make an iterator?

Sigh. Either I have all the didactic talent of your average post-graduate tutorial host, or you've not read my post correctly.

What I said was that to use iterators generically, and without iterators sharing a common interface (e.g.
IObjectEnumerator, or IEnumerator!(int)), we need implicit instantiation.

> Maybe I'm missing the boat, but what's wrong with something like this:
>
> class List(T) {
>    alias ListIterator(T) iterator;
>
>    ...
> }
>
> List!(int).iterator iter;
> // Hell you could probably even take your existing instance and do:
> // Foo.iterator iter;  In fact you should be able to but it seems you
> can't  This would allow you to also do aliases for stuff like:
>
> iter.BaseType t; and do your generic manipulation functions. or List.BaseType; etc.  And have aliases in your class.

This kind of thing is already done. One can (or at least could - I've changed a lot of things since April, when I last built and tested with -version=UseIterators) do things such as:

    List!(int)    l = new ...

    for(List!(int).iterator b = l.begin(), e = l.end(); b != e; b.next())
    {
        int i = b.value();
    }

But one cannot do something such as

    int j = accumulate(l.begin(), l.end(), 0);

To do this, one has to provide the type(s), as in:

    int j = accumulate!(List!(int).iterator, int)(l.begin ...)

[Actually, since all iterators must be classes, I've prescribed that they all have a standard set of member types, so it's possible to deduce the value type, as in:

    int j = accumulate!(List!(int).iterator)(l.begin ...)
]

Given that the (currently) only useful mode for iterators is where the type is explicit, it's my opinion (and that of others, I think) that there's no benefit over foreach, hence the focus on foreach for the container enumeration, and the enumeration of transformed ranges, as in:

    foreach(int i; l)
    {
        . . .
    }

and

    foreach(int i; l.select(IsOdd))
    {
        . . .
    }

>
> I say we bitch at walter.
>
> for( iter = myList.begin();  iter != myList.end(); iter++ )
> {
>    printf(iter.getMyFuckingInstanceNow().toString());
> }
>
> This is nearly identical to what C++ does, and i really don't see why it wouldn't work.

It can work. It does work. I've mentioned this many times on the NGs.

>  You could probably leave the instance-getter function
> name a little shorter though.

D'ya think?

> Maybe you could expand as to why this sucks? (Besides that it's similar
> to STL which sucks)

Again, a pointless comment that suggests ignorance, or a fondness for glib statements at best. STL is a mostly marvellous idea/technology, but the bits of it that are bad are glaringly bad. It does not suck, it's the current best effort at powerful generic programming. Just because something has bad parts doesn't mean it sucks. If that's the case, then everything sucks.

It's my hope and, I would think, that of most people in D, that DTL can be (nearly) as powerful as STL, but jettison most of the glaringly bad parts. I don't expect DTL to be without flaws, but it needs to be simpler, and less prone to spawning dialects, than STL. And it needs more power than the language currently provides.

> And as for your template example:
>
>
> #    template dump(T) {
> #    void dump(IContainer!(T) c)
> #    {
> #        for(IEnumerator e = c.enumerate(); e.hasMoreElements; )
> #        {
> #
> #        }
> #    }
> #    }
> #
> #    List!(int, IContainer!(int))           l = new ...;
> #
> #    dump(l);
>
> For one, making those aliases all over is just as ugly and hard to remember as just specifying it.  Second:
>
> You're passing in l of type List!(int, IContainer!(int)), and then
> proceed to do this with it:
>
> IContainer!(List!(int, IContainer!(int))) c.
>
> Is that really what you were intending to do?

I'm afraid I can't follow the last section. Can you rephrase?

> It seems like templates should support something along these lines:
>
> #   template dump( T : IContainer!(T) ) {
> #      void dump( IContainer!(t) ) {
> #      }
> #   }
>
> Although I seriously doubt that works as it is.

What's "t"? If you mean "T", then I presume the different you're providing is the constraint on T to be "in" an IContainer!(T)?? Doesn't the function signature already do that? Please elucidate.



July 29, 2004
Walter wrote:

> I hadn't realized that structs and classes cannot be discriminated in
> template specialization, you're right. How does the following look:
>     template foo(T : struct)
> and:
>     template foo(T : class)
> ?

Oh my god, that's the most beautiful thing I've ever seen!

...in the context of programming languages of course :) It would complement the existing specialization rules so nicely.
July 29, 2004
"Walter" <newshound@digitalmars.com> wrote in message news:cebikp$1mh0$1@digitaldaemon.com...
>
> "Matthew" <admin.hat@stlsoft.dot.org> wrote in message news:ceaeid$18ir$1@digitaldaemon.com...
> > I'm almost at the end of my tether here. There are so many problems with
> the language/compiler, that I'm wondering
> > seriously whether it's going to be possible to have a decent generic
> template library.
>
> I think that's to be expected with a new generic language design. After all, the creator of STL (Stepanov?) went through multiple design iterations with Stroustrup. None of us are smart enough to get it all right the first, second, or even third time.

I accept all that. I guess I had to get a bit tabloid to get your attention. ;)

Nonetheless, I want us to start out with DTL a level above STL, since we'd be foolish not to learn from our experience with it, no?

>You do have a tendency to push the envelope past
> its limits (I see that in your C++ code!), and that's good for finding what those limits and weaknesses are.

Hah! So much for me thinking that I use C++ in a sensible, simple, minimalist way. :-(

> Generic programming in D is also going to be in a different style than in C++, and use different techniques.

I couldn't agree more.

> We have to carefully think about whether
> the problems are bugs in the compiler,

Definitely

> bugs in the language design,

Probably

> or bugs
> in our thinking

Not a chance. :)

> - perhaps we haven't discovered the right D way to do generic programming.

In terms of using and manipulating the elements in containers, as opposed to manipulating containers themselves (i.e. adding/removing/moving elements), then I believe we do have the right approach: foreach.

Since I've devised ways to be able to apply filters/transformation to containers that are themselves foreach-able (and can also be subject to filters/transformations themselves), then I believe we are on the right road, or at least one of several right roads.

As for manipulating containers themselves, this either needs iterators, or modifying interfaces, to do in a generic manner. The former are impossible/difficult in D, and the latter are, IMO, unattractive. In fact, as a huge devotee of STL, I must say that I virtually never modiy containers generically, beyond using the obvious one of back_inserter. Since DTL containers can be parameterised to derive from interfaces, it's not going to be hard to have them derive from a common interface that supports the add() method. (In fact I played around with this some months, ago, and it worked _reasonably_ well)

So, I would (impartially) suggest that if the compiler/language could support what I've been trying to achieve in the last few months, i.e. composable transformations, without the various hassles, then this would be a big step towards container nirvana.

> One thing I am pretty sure of, the C++ way of doing STL isn't the right way for D.

I agree.

> I personally find STL to be impenetrably obtuse.

I don't mind STL so much, as I'm reasonably on board with it. And there are a couple of wonderfully powerful yet discoverable STL extension libraries out there ...

But I agree that much STL-like code now being produced is totally inacessible. It's little more than intellectual masturbation.

> I refuse to believe that is the best way of doing generics. I think it's great that you are taking a different approach than STL does, and are open to inventive new ways of thinking about it.

Why thank you. ;)

> > Alas, this crashes the compiler. Sigh ...
>
> That's my problem, send me the test case!

That's my problem. There is no test case. There's the DTL implementation. I don't know that I have the skill to trim it down, but I'm absolutely sure I don't have the time. (I'm now doing a rather time-tight contract for a real paying client, so am kind of busy. I spent much of yesterday doing DTL - don't tell my wife!! - so this w/e I'll be playing catch up on my "real" work.)

I really think you need to come up with a better way to diagnose flaws in the compiler, such that we can just send you dumps. You're a super smart guy. I know you can do this. And I reckon this'll save masses of time all round in the long run

> > 4. Iterator based approach. There are two reasons why STL-iterators are
> not possible in D: (i) there is no implicit
> > instantiation, and (ii) there is no operator *(). Neither of these
> preclude the creation of iterators - and I've done a
> > little of that - including adaptors for arrays/pointers, but it means
> they're not really very usable.
>
> Not having implicit instantiation can make the code a bit more verbose, but it shouldn't be that bad.

You can't write and apply generic code to a given instantion of a container/iterator/range/enumerator without explicit stating its type. That is not generic programming; that's merely code reuse.

> As for operator*(), why should that be a problem?
> Just define a member function called, I'm sure there's a better name,
> operatorStar(), and instead of *foo, type foo.operatorStar().

I musn't have been clear, as several people have made this misunderstanding.

The iterator types that are currently coded have the following interface:

    class SomeIterator
    {
            void next()
            value_type value()
            int opEquals(class_type rhs)
    }

The point is that without op*(), we cannot support pointers as iterators. As a library implementor, that's actually a blessing, since I can now prescribe that all iterators exhibit certain member types, which means that some of the worst crimes of STL (actually, they're crimes of badly implemented standard libraries, mentioning no names ...) can be avoided.

>
> > 1. Beyond a significant, but indefinable level of complexity, the
> linker/compiler loose the ability to find all required
> > symbols, while chopping out any *unrelated* parts makes them locatable
> again. Boil it down? I wish!!
> > Nonetheless, that's a bug in the compiler/linker, and doesn't get my blood
> up, since one can usually find workarounds,
> > and at this stage a little bugginess is not the crime of the century.
>
> If you can't reduce it, you can't reduce it. Send a reproducible example.
>
>
> > 2. Templates in D are instantiated en masse. What "en masse" actually
> means is beyond me, since I am yet to work out the
> > true rules regarding instantation.
>
> The true rule is the whole template is compiled when it is instantiated. (This is unlike C++, which does "lazy instantiation", which means it only compiles the bits of a template that are actually used.)
>
> > But what I can say is that having a template such as the following is
> totally
> > f**cked:
> >
> >     template TransformedRange(R, F, T) { class TransformedRange
> >      : public NotionalRange!(T)
> >     {
> >       . . .
> >         this(R r, F f)
> >         {
> >             m_r = r.dup;
> >             m_f = f;
> >         }
> >         this(R r)
> >         {
> >             m_r = r.dup;
> >             m_f = new filter_type();
> >         }
> >
> > Without worrying too much about the details of what a TransformedRange
> does, the problem is pretty obvious. If F does
> > not have a default constructor, one cannot instantiate TransformedRange
> even in the case where the single parameter ctor
> > *is never called*!
>
> I don't get it from the example given.

The example had a flaw. F === filter_type. (I forgot to change the last filter_type).

Basically, if F does not have a default constructor, then the compiler craps on

             m_f = new F(); << this line

> > 3. There's no implicit instantiation. This has *massive* consequences,
> including:
> >
> > - We can't have iterators (as noted above). This is fine for reading from
> containers, but consider how we might
> > "generically" insert into container ranges in the same (or any analogous)
> way as is the case with STL.
>
> Can you give a canonical example?
>
>
> > - In order to support the parameterisable interface (e.g.
> IContainer!(int)) described above, there needs to be a common
> > way to manipulate built-in types, objects and structs. For some things,
> one can use traits, for others, overloaded
> > functions. Alas, there seems to be no way to discriminate structs via
> overloaded functions. Hence, currently the DTL
> > containers do not support polymorphic interfaces when storing structs.
> Naturally, this has much wider consequences
>
> I hadn't realized that structs and classes cannot be discriminated in
> template specialization, you're right. How does the following look:
>     template foo(T : struct)
> and:
>     template foo(T : class)
> ?

You know me: how it looks is secondary to it working, and not having nasty corner cases. How would such a syntax fit with existing template parameter constraints?

> > 4. D's import stuff just blows my mind! As a related issue to the boxing
> utility class described above, I'm running into
> > conflicts between the toString() functions in std.string and my box and
> boxutil modules. I've tried all kinds of use of
> > private imports, to no avail. I concede that this might be my fault, and I
> might have just failed to grok D's import
> > rules, but as it currently looks to me, it looks bad and stupid.
>
> I suspect the problem you're running into is that there's a toString in std.string, and a toString in foo. Then, in bar.d:
>
>     import std.string;
>     import foo;
>
>     ...
>     x = toString(y);
>     ...
>
> This will bring up a conflict, because which toString do you want, the one in std.string or the one in foo? The compiler doesn't know. The rule is that name lookup happens first, *then* overload matching. If you want to overload them together:
>
>     import std.string;
>     import foo;
>     alias std.string.toString  toString;
>     alias foo.toString  toString;
>
>     ...
>     x = toString(y);
>     ...
>
> now there's no longer a conflict, because toString is found in the current module (the aliases), and the aliases are chased down for overload resolution. It is this way because it gives you complete control over overloading.

Hmm. I'll have to think about that one after my morning ride. My first reaction is a what? with a capital W

> > So, what do we do about this? Walter's stock response is to boil it down,
> but it's either impossible to do so with
> > non-trivial projects such as DTL, or I'm simply not smart enough to do it.
> Sure, one might argue that this is indicative
> > of a too-complex design, but I think that's crap: the concept is simple,
> and the code is simple; it just doesn't work.
> > (Very similar techniques that I've experimented on in C++ work fine.)
>
> If you can't do it, you can't do it. Send what you can do.
>
> > Rather than having things boiled down, I think the compiler should be
> amended to provide *copious" debugging
> > information, so we can email a dump of that to Walter, and which will be
> useful to him.
>
> Trust me, sending me dumps would be quite useless.
>
>


July 29, 2004
"Walter" <newshound@digitalmars.com> wrote in message news:cebm6f$1o79$1@digitaldaemon.com...
>
> "C. Sauls" <ibisbasenji@yahoo.com> wrote in message news:cebjlk$1n2q$1@digitaldaemon.com...
> > Walter wrote:
> > > I hadn't realized that structs and classes cannot be discriminated in
> > > template specialization, you're right. How does the following look:
> > >     template foo(T : struct)
> > > and:
> > >     template foo(T : class)
> > > ?
> >
> > Looks promising to me... how likely would the following be, as well?
> > template foo(T : signed)
> > template foo(T : unsigned)
> > template foo(T : enum)
> >
> > I figure the more ways to narrow down the parameters, the better.  Maybe
> > templates could be further revised to have complex filters, such as:
> > template foo(T : struct || (signed && enum))
>
> It's a good idea. I've been thinking something along those lines for 2.0.

Think 1.0!



July 29, 2004
"Matthew" <admin@stlsoft.dot.dot.dot.dot.org> wrote in message news:cebp7v$1pt7$2@digitaldaemon.com...
> This kind of thing is already done. One can (or at least could - I've
changed a lot of things since April, when I last
> built and tested with -version=UseIterators) do things such as:
>
>     List!(int)    l = new ...
>
>     for(List!(int).iterator b = l.begin(), e = l.end(); b != e; b.next())
>     {
>         int i = b.value();
>     }
>
> But one cannot do something such as
>
>     int j = accumulate(l.begin(), l.end(), 0);

That's a compiler bug, now fixed. At least I think this should work now <g>.



July 29, 2004
In article <cebjlk$1n2q$1@digitaldaemon.com>, C. Sauls says...
>
>Looks promising to me... how likely would the following be, as well?
>	template foo(T : signed)
>	template foo(T : unsigned)
>	template foo(T : enum)
>
>I figure the more ways to narrow down the parameters, the better.  Maybe
>templates could be further revised to have complex filters, such as:
>	template foo(T : struct || (signed && enum))

This would be fantastic.  It would single-handedly eliminate a large bulk of meta garbage classes.


Sean


July 29, 2004
In article <cebrh0$1qnl$1@digitaldaemon.com>, Walter says...
>
>"Matthew" <admin@stlsoft.dot.dot.dot.dot.org> wrote in message news:cebp7v$1pt7$2@digitaldaemon.com...
>> This kind of thing is already done. One can (or at least could - I've
>changed a lot of things since April, when I last
>> built and tested with -version=UseIterators) do things such as:
>>
>>     List!(int)    l = new ...
>>
>>     for(List!(int).iterator b = l.begin(), e = l.end(); b != e; b.next())
>>     {
>>         int i = b.value();
>>     }
>>
>> But one cannot do something such as
>>
>>     int j = accumulate(l.begin(), l.end(), 0);
>
>That's a compiler bug, now fixed. At least I think this should work now <g>.

Okay, now I'm confused.  Matthew's example seems to be that implicit type discovery doesn't work with templates.  Surely this can't be the bug that was fixed.


Sean


July 29, 2004
Sean Kelly wrote:

> In article <cebjlk$1n2q$1@digitaldaemon.com>, C. Sauls says...
>>
>>Looks promising to me... how likely would the following be, as well?
>>template foo(T : signed)
>>template foo(T : unsigned)
>>template foo(T : enum)
>>
>>I figure the more ways to narrow down the parameters, the better.  Maybe
>>templates could be further revised to have complex filters, such as:
>>template foo(T : struct || (signed && enum))
> 
> This would be fantastic.  It would single-handedly eliminate a large bulk of meta garbage classes.

Another strange idea, extending it to classes:

template foo(T : ForwardIterable, BackwardIterable, Addable)

Of couse ForwardIterable, BackwardIterable and Addable would be interfaces that the foo parameter must implement.
July 29, 2004
Juanjo Álvarez wrote:

> Sean Kelly wrote:
> 
>> In article <cebjlk$1n2q$1@digitaldaemon.com>, C. Sauls says...
>>>
>>>Looks promising to me... how likely would the following be, as well?
>>>template foo(T : signed)
>>>template foo(T : unsigned)
>>>template foo(T : enum)
>>>
>>>I figure the more ways to narrow down the parameters, the better.  Maybe
>>>templates could be further revised to have complex filters, such as:
>>>template foo(T : struct || (signed && enum))
>> 
>> This would be fantastic.  It would single-handedly eliminate a large bulk of meta garbage classes.
> 
> Another strange idea, extending it to classes:
> 
> template foo(T : ForwardIterable, BackwardIterable, Addable)
> 
> Of couse ForwardIterable, BackwardIterable and Addable would be interfaces that the foo parameter must implement.

And of course that syntax would not work because it takes the second and third interfaces as new parameters :). Anyway I've re-readed a little of the spec to find that you can do:

interface ForwardIterable {  void fwiterate(); }
interface BackwardIterable { void bwiterate(); }
interface Addable          { /*opAdd and stuff */ }
//Ugly
iterface FwBwIterableAddable : ForwardIterable, BackwardIterable, Addable {}

class A : FwBwIterableAddable {
        void fwiterate() { return 0;}
        void bwiterable() { return 0;}
}

template TFoo( T : FwBwIterableAddable ) {}

main() { alias TFoo!(A)x; }

But as you can see you have to declare an interface which inherits from all the interfaces and then declare the class to inherit from that interface to be allowed to limit the template parameter. Same effect, but uglier than just specifying a list of interfaces to the template parameter.

Anyway I've done little D and none of that was very generic so I could be being really naive here (not that I care, that's one of my features ;).
July 29, 2004
"Sean Kelly" <sean@f4.ca> wrote in message news:cebtmu$1ro6$1@digitaldaemon.com...
> In article <cebrh0$1qnl$1@digitaldaemon.com>, Walter says...
> >
> >"Matthew" <admin@stlsoft.dot.dot.dot.dot.org> wrote in message news:cebp7v$1pt7$2@digitaldaemon.com...
> >> This kind of thing is already done. One can (or at least could - I've
> >changed a lot of things since April, when I last
> >> built and tested with -version=UseIterators) do things such as:
> >>
> >>     List!(int)    l = new ...
> >>
> >>     for(List!(int).iterator b = l.begin(), e = l.end(); b != e; b.next())
> >>     {
> >>         int i = b.value();
> >>     }
> >>
> >> But one cannot do something such as
> >>
> >>     int j = accumulate(l.begin(), l.end(), 0);
> >
> >That's a compiler bug, now fixed. At least I think this should work now <g>.
>
> Okay, now I'm confused.  Matthew's example seems to be that implicit type discovery doesn't work with templates.  Surely this can't be the bug that was fixed.

I'm with Sean on this one. Since D doesn't have implicit instantion, how can an accumulate template be instantiated as shown above?