December 18, 2009
On Thu, Dec 17, 2009 at 11:41 AM, bearophile <bearophileHUGS@lycos.com> wrote:
> dsimcha:
>
>>The only solution I see would be to completely get rid of separate compilation.<
>
> "All problems in computer science can be solved by another level of indirection;"
> -- David Wheeler
> But that also slows code down a little :-)
>
> --------------------
>
> Bill Baxter:
>
> Static interfaces are an easy idea, it was discussed some weeks ago, and probably it's not too much hard to implement in the language. I like them enough, but they don't add that much to the template constraints already present, so it's mostly a duplication of syntax and semantics. So I am not sure they are a good idea.

My reason for bringing them up was to say that perhaps they could be carried beyond that simple idea.


>>static interface VConst(T) {
>   constnessOf(T) is in [const, immutable, !const];
> }
> VConst!(Bar) doSomething(VConst!(Bar) b, int y) {...}
>
> static interface MaybeRef(T) {
>   refnessOf(T) is in [ref, !ref];
> }
> MaybeRef!(Bar) doSomethingElse(MaybeRef!(Bar) b, int y) { ... }<
>
> Walter has just added traits to perform this, so I think this is already doable, with __trait/meta. and template constraints.
> The opIn_r defined for arrays is something that D2 must eventually have, there's no doubt about this.
>
> But "is in", followed by an array of those modifiers is currently impossible (you may create a tuple of templates, where each template tests for constness, etc). Maybe in future you can create an array of annotations:
> [@const, @immutable, @notConst]

Yeh, I know what I wrote is impossible now.  Just putting the idea out there because it seems like we're heading for a place where we solve the problem of generating multiple versions of a function in three completely different ways (auto ref, vconst and of course templates) [four if you count generating them with string mixins].  I don't know what the proper generalization is, but I think if you try to keep templates the way they are while generalizing them to handle these cases you will end up with an unworkable notation nightmare.  I like the way Go static interfaces refactor template specifications to simplify the overall syntax.

In WalterAndrei.pdf from the D conference there was talk of static parameters and of unifying regular functions and templates.  The Go approach basically accomplishes that by associating a symbol with all the template specification baggage.  Then just by using that symbol in the parameter list of an ordinary-looking function, the compiler can tell not only 'hey we've got a template' but also what types of constraints apply.

In a sense, current templates are analogous to structures that require
you to spell out the members of the struct every time you use one.
For structs we can do:
   struct Complex { float re; float im; };
   Complex addComplex(Complex a, Complex b);

But imagine if you had to write that always as
    template isComplex(T) {
          enum isComplex =
                   __traits(hasMember,T,"re") &&
                   __traits(hasMember,T,"im") &&
                    typeof(T.init.im == float) && typeof(T.init.re == float);
    }
    T addComplex(T)(T a, T b) if (IsComplex!(T)) { ... }

The current system forces you to do that the instant your constraint becomes more complex than "a struct that has these members of these types in this particular order".    Instead, Go encodes the complexity in a reusable symbol that acts like a type name, but it contains richer, more generic information than a regular type.

A big thing lacking is that Go doesn't give you a way to express
constraints between types.  Like with:
      T index(T,S)(T[S] x, S y) { ... }

But I think you could devise a way to encompass such cases.  Maybe something like:

      static interface Indexable  {
            Indexed := Indexable[Index];
      }
      Indexable.Indexed index(Indexable x, Indexable.Index y) { ... }

(Note this is more general than the above T[S] template which only
matches built-in AA's)

This is clearly too much for D2 to swallow at this point.  But I think it's worth thinking about whether there may be a significantly better way to specify parametrized types and constraints on them than what C++ and D use.

--bb
December 19, 2009
Bill Baxter:
> I don't know
> what the proper generalization is, but I think if you try to keep
> templates the way they are while generalizing them to handle these
> cases you will end up with an unworkable notation nightmare.

It's not easy to design such large changes up-front (Andrei has for example done it with Ranges, but only few people have enough brain to do that and produce a final result that's useful in practice), so I think the design strategy used here is: try to generalize templates to address those cases, produce a too much high castle of cards that you can see is unstable, then try to fix the mess redesigning using a more clean design. The key here is to keep the D3 design process flexible enough, so you can fix design mistakes along the way ;-) It's an iterative process of generalization & abstraction.


> In WalterAndrei.pdf from the D conference there was talk of static parameters and of unifying regular functions and templates.

I think Walter has abandoned that idea, for never specified implementation difficulties. I'd like to know more about those difficulties (but I agree that if an implementation is too much hard to do compared to the gains it gives, then it's better to abandon it).


> But I think
> it's worth thinking about whether there may be a significantly better
> way to specify parametrized types and constraints on them than what
> C++ and D use.

Of course. But you may need to implement a more powerful type system to do that.

Bye,
bearophile
1 2 3 4
Next ›   Last »