www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Unification and extension of compile-time reflection

reply "Jarrett Billingsley" <jarrett.billingsley gmail.com> writes:
Once upon a time, D did not have string mixins, or CTFE, or templates,
or any of the fun things we have today.  Even so, it was still
important to be able to access some information about types.  So
Walter made it possible to query some basic information - the
initialization value, minimum and maximum allowable values, byte size
and alignment etc. - directly from the types as properties.  Neat.

Then D got templates.  D's templates were based on C++'s, to an
extent, and therefore made use of specialization (and by corollary,
SFINAE) to determine which template to instantiate.  By their nature,
templates are a sort of way of introspecting types.  So now D has two
ways to find out things about types.  Okay.

It turned out that templates were not always powerful enough - or
sufficiently concise - to express some ideas.  So Walter came up with
the is() expression (originally the "iftype" statement which begat
"is()" and "static if") to query other, more interesting information
about types.  It's _kind of_ like template specialization but has some
extra features that specialization doesn't.  Now D has three ways to
find out things about types.  Hm.

Along comes D2, and with it, the __traits keyword.  __traits is
wonderful (except for the double-underscore name, anyway).  It's
extensible, flexible, and can answer queries about types and other
program objects in a way that would be extremely convoluted or cryptic
if templates or the is() expression were extended.

But now D programs have _four_ ways to ask questions about themselves.

Some of these methods overlap but with wildly different syntax.  Many
questions have to be composed out of these four disparate methods in
unintuitive manners.  Walter's "Templates Revisited" article says that
"[m]any useful aspects of C++ templates have been discovered rather
than designed," but in all honesty, this is exactly the situation with
D's compile-time introspection.  D's templates are better, yes, but
the problem has simply been promoted to a wider scope.

So what can we do?  I've been thinking about it and I think that
__traits, coupled with the new template constraints, can handle just
about everything.  Does that mean we ditch all the other syntax?  Not
necessarily - it's just that __traits can be the _backend_ for many
other features.

First of all __traits' name has to be revised.  The double-underscore
just isn't working for me.  I think it would be a fair tradeoff to
rename it "traits" and rename the std.traits and core.traits modules
something else.  Come on Walter, I know that adding keywords is
undesirable, but ffs, at some point _not_ adding keywords is just as
bad.  D does not have a very large user or codebase, and if you're
going to break backwards compatibility - _break it now_, before it's
too late.  (besides, some of the keyword mass will need to be
redistributed when imaginary/complex types are removed ;) )

Secondly - the type properties are cute but they're not very flexible.
 They can interfere with fields and methods, and so the compiler has
to explicitly check that aggregate member names don't step on the
built-in property names.  I think that "T.prop" could just be replaced
with "traits(prop, T)".  traits(min, int), and so on.  Yes, it's
longer - but that's what templates are for, if you really want it
shorter: Min!(int).

Third, the is() expression is greatly overstepping its bounds now that
traits is around.  Why is there "is(T == class)", but
__traits(isAssociativeArray, T)?  The is(T) form can be replaced by
__traits(compiles).  The is(T : U) form can be replaced by
traits(convertible).  The is(T == U) forms can all be replaced by
traits(equivalent) and traits(isClass) and the like.  The strange is(T
U), is(T U : V), is(T U == V) forms.. I'm not sure what to do about
those.  The ones like is(T U == return) are an obvious abuse and
should be replaced with traits(returnType) or the like.

The fourth and final issue is template specialization.  This one
really does have too much inertia to remove.  So what I propose for
this is that is() -- now that it has the ability to perform just about
everything that template specialization can -- should become the
backend for template specializations.  That is, something like:

template Foo(T, U : V[K], K, V)
{
...
}

is just a shorter way of writing something like:

template Foo(T, U) if(is(U : V[K]))
{
...
}

I don't think template constraints currently introduce symbols into
the template body, but this would declare V and K as types within the
body of Foo.

But here's the kicker: even is() is not an entirely basic construct.
It's more or less a shortcut for more complex traits expressions
combined with the ability to alias traits to symbols.  That is, "is(U
: V[K])" is like the pseudocode "traits(isAssociativeArray, U) &&
alias traits(itemType, U) V && alias traits(indexType, U) K".  Of
course you can't put aliases in expressions, but the overall idea is
that this is() expression is the same as using isAssociativeArray and
then aliasing other traits as K and V.

Why do this?  Simplicity, generality, and consistency.  Once all this
is done, it becomes easy to see what questions can and can't be asked
about your code.  You only have to look in one place: traits.
Everything else is defined in terms of it.

...

So what do you think?
Nov 24 2008
next sibling parent BCS <ao pathlink.com> writes:
Reply to Jarrett,

 The strange is(T U), is(T U : V), is(T U == V) forms.. I'm not sure
 what to do about those.
 

#2 & 3 are to allow compact type pattern matching. IIRC there not that good at it, but are better than nothing. It's more or less what you bring up in you last point. Any solution here should include the ability to ask "does type T match pattern P if the symbols A B and C (that are part of P) are defined correctly? If so, declared A, B and C as needed". You sort of hint at that ability near the end, but it needs to be explicit in the design goals.
Nov 24 2008
prev sibling next sibling parent reply "Jarrett Billingsley" <jarrett.billingsley gmail.com> writes:
On Mon, Nov 24, 2008 at 6:55 PM, BCS <ao pathlink.com> wrote:
 Reply to Jarrett,

 The strange is(T U), is(T U : V), is(T U == V) forms.. I'm not sure
 what to do about those.

#2 & 3 are to allow compact type pattern matching. IIRC there not that good at it, but are better than nothing. It's more or less what you bring up in you last point. Any solution here should include the ability to ask "does type T match pattern P if the symbols A B and C (that are part of P) are defined correctly? If so, declared A, B and C as needed". You sort of hint at that ability near the end, but it needs to be explicit in the design goals.

OK, explicitly then: those forms remain. ;)
Nov 24 2008
parent BCS <ao pathlink.com> writes:
Reply to Jarrett,

 ;)
 

:b
Nov 24 2008
prev sibling next sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Jarrett Billingsley escribió:
 Once upon a time, D did not have string mixins, or CTFE, or templates,
 or any of the fun things we have today.  Even so, it was still
 important to be able to access some information about types.  So
 Walter made it possible to query some basic information - the
 initialization value, minimum and maximum allowable values, byte size
 and alignment etc. - directly from the types as properties.  Neat.
 
 Then D got templates.  D's templates were based on C++'s, to an
 extent, and therefore made use of specialization (and by corollary,
 SFINAE) to determine which template to instantiate.  By their nature,
 templates are a sort of way of introspecting types.  So now D has two
 ways to find out things about types.  Okay.
 
 It turned out that templates were not always powerful enough - or
 sufficiently concise - to express some ideas.  So Walter came up with
 the is() expression (originally the "iftype" statement which begat
 "is()" and "static if") to query other, more interesting information
 about types.  It's _kind of_ like template specialization but has some
 extra features that specialization doesn't.  Now D has three ways to
 find out things about types.  Hm.
 
 Along comes D2, and with it, the __traits keyword.  __traits is
 wonderful (except for the double-underscore name, anyway).  It's
 extensible, flexible, and can answer queries about types and other
 program objects in a way that would be extremely convoluted or cryptic
 if templates or the is() expression were extended.
 
 But now D programs have _four_ ways to ask questions about themselves.
 
 Some of these methods overlap but with wildly different syntax.  Many
 questions have to be composed out of these four disparate methods in
 unintuitive manners.  Walter's "Templates Revisited" article says that
 "[m]any useful aspects of C++ templates have been discovered rather
 than designed," but in all honesty, this is exactly the situation with
 D's compile-time introspection.  D's templates are better, yes, but
 the problem has simply been promoted to a wider scope.
 
 So what can we do?  I've been thinking about it and I think that
 __traits, coupled with the new template constraints, can handle just
 about everything.  Does that mean we ditch all the other syntax?  Not
 necessarily - it's just that __traits can be the _backend_ for many
 other features.
 
 First of all __traits' name has to be revised.  The double-underscore
 just isn't working for me.  I think it would be a fair tradeoff to
 rename it "traits" and rename the std.traits and core.traits modules
 something else.  Come on Walter, I know that adding keywords is
 undesirable, but ffs, at some point _not_ adding keywords is just as
 bad.  D does not have a very large user or codebase, and if you're
 going to break backwards compatibility - _break it now_, before it's
 too late.  (besides, some of the keyword mass will need to be
 redistributed when imaginary/complex types are removed ;) )
 
 Secondly - the type properties are cute but they're not very flexible.
  They can interfere with fields and methods, and so the compiler has
 to explicitly check that aggregate member names don't step on the
 built-in property names.  I think that "T.prop" could just be replaced
 with "traits(prop, T)".  traits(min, int), and so on.  Yes, it's
 longer - but that's what templates are for, if you really want it
 shorter: Min!(int).
 
 Third, the is() expression is greatly overstepping its bounds now that
 traits is around.  Why is there "is(T == class)", but
 __traits(isAssociativeArray, T)?  The is(T) form can be replaced by
 __traits(compiles).  The is(T : U) form can be replaced by
 traits(convertible).  The is(T == U) forms can all be replaced by
 traits(equivalent) and traits(isClass) and the like.  The strange is(T
 U), is(T U : V), is(T U == V) forms.. I'm not sure what to do about
 those.  The ones like is(T U == return) are an obvious abuse and
 should be replaced with traits(returnType) or the like.
 
 The fourth and final issue is template specialization.  This one
 really does have too much inertia to remove.  So what I propose for
 this is that is() -- now that it has the ability to perform just about
 everything that template specialization can -- should become the
 backend for template specializations.  That is, something like:
 
 template Foo(T, U : V[K], K, V)
 {
 ...
 }
 
 is just a shorter way of writing something like:
 
 template Foo(T, U) if(is(U : V[K]))
 {
 ...
 }
 
 I don't think template constraints currently introduce symbols into
 the template body, but this would declare V and K as types within the
 body of Foo.
 
 But here's the kicker: even is() is not an entirely basic construct.
 It's more or less a shortcut for more complex traits expressions
 combined with the ability to alias traits to symbols.  That is, "is(U
 : V[K])" is like the pseudocode "traits(isAssociativeArray, U) &&
 alias traits(itemType, U) V && alias traits(indexType, U) K".  Of
 course you can't put aliases in expressions, but the overall idea is
 that this is() expression is the same as using isAssociativeArray and
 then aliasing other traits as K and V.
 
 Why do this?  Simplicity, generality, and consistency.  Once all this
 is done, it becomes easy to see what questions can and can't be asked
 about your code.  You only have to look in one place: traits.
 Everything else is defined in terms of it.
 
 ...
 
 So what do you think?

I liked the way you wrote this. :-) I think neither __traits nor a property is good enough for compile-time reflection. I think just one property is enough. For example in Java you do: someInstance.getClass() and then you enter "the reflection world", which uses the same language as Java, but it's at a different level. So: var.reflect or something like that would be awesome. Then you can do: - something.reflect.methods - something.reflect.isVirtual - something.reflect.isAbstract - something.reflect() // same as something.reflect.compileTimeValue - something.reflect.fields - etc. So you just don't allow "reflect" (or whatever) as a field name (if you define it, it's an error, much like "sizeof"), but once you enter "reflect" the compiler can add as many name as it wants, nobody can override these. "reflect" is smart so that for an expression, it return a specific (compile-time) type; for classes, another (compile-time) type; for variables, another (compile-time) type; etc.
Nov 24 2008
next sibling parent Lars Ivar Igesund <larsivar igesund.net> writes:
Nick Sabalausky wrote:

 "Bill Baxter" <wbaxter gmail.com> wrote in message
 news:mailman.54.1227583827.22690.digitalmars-d puremagic.com...
 On Tue, Nov 25, 2008 at 11:48 AM, Ary Borenszweig <ary esperanto.org.ar>
 wrote:
 I liked the way you wrote this. :-)

 I think neither __traits nor a property is good enough for compile-time
 reflection. I think just one property is enough.

 For example in Java you do:

 someInstance.getClass()

 and then you enter "the reflection world", which uses the same language
 as
 Java, but it's at a different level.

 So:

 var.reflect

 or something like that would be awesome. Then you can do:

 - something.reflect.methods
 - something.reflect.isVirtual
 - something.reflect.isAbstract
 - something.reflect() // same as something.reflect.compileTimeValue
 - something.reflect.fields
 - etc.

 So you just don't allow "reflect" (or whatever) as a field name (if you
 define it, it's an error, much like "sizeof"), but once you enter
 "reflect"
 the compiler can add as many name as it wants, nobody can override
 these. "reflect" is smart so that for an expression, it return a
 specific (compile-time) type; for classes, another (compile-time) type;
 for variables, another (compile-time) type; etc.

Or you could just call it traits. something.traits.methods something.traits.max something.traits.sizeof (1+34.).traits.typeof I do like the general idea of unifying this stuff. Can you make an alias or variable of this .traits/.reflect type? Can it return a metaclass type of some sort so that an alias would be possible? I.e. alias t = something.traits;

That would make it much easier to port over Java code that uses reflection. And even aside from that, I think it's a very nice and clean solution.

vote++ -- Lars Ivar Igesund blog at http://larsivi.net DSource, #d.tango & #D: larsivi Dancing the Tango
Nov 25 2008
prev sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Bill Baxter wrote:
 On Tue, Nov 25, 2008 at 11:48 AM, Ary Borenszweig <ary esperanto.org.ar> wrote:
 Jarrett Billingsley escribió:
 Once upon a time, D did not have string mixins, or CTFE, or templates,
 or any of the fun things we have today.  Even so, it was still
 important to be able to access some information about types.  So
 Walter made it possible to query some basic information - the
 initialization value, minimum and maximum allowable values, byte size
 and alignment etc. - directly from the types as properties.  Neat.

 Then D got templates.  D's templates were based on C++'s, to an
 extent, and therefore made use of specialization (and by corollary,
 SFINAE) to determine which template to instantiate.  By their nature,
 templates are a sort of way of introspecting types.  So now D has two
 ways to find out things about types.  Okay.

 It turned out that templates were not always powerful enough - or
 sufficiently concise - to express some ideas.  So Walter came up with
 the is() expression (originally the "iftype" statement which begat
 "is()" and "static if") to query other, more interesting information
 about types.  It's _kind of_ like template specialization but has some
 extra features that specialization doesn't.  Now D has three ways to
 find out things about types.  Hm.

 Along comes D2, and with it, the __traits keyword.  __traits is
 wonderful (except for the double-underscore name, anyway).  It's
 extensible, flexible, and can answer queries about types and other
 program objects in a way that would be extremely convoluted or cryptic
 if templates or the is() expression were extended.

 But now D programs have _four_ ways to ask questions about themselves.

 Some of these methods overlap but with wildly different syntax.  Many
 questions have to be composed out of these four disparate methods in
 unintuitive manners.  Walter's "Templates Revisited" article says that
 "[m]any useful aspects of C++ templates have been discovered rather
 than designed," but in all honesty, this is exactly the situation with
 D's compile-time introspection.  D's templates are better, yes, but
 the problem has simply been promoted to a wider scope.

 So what can we do?  I've been thinking about it and I think that
 __traits, coupled with the new template constraints, can handle just
 about everything.  Does that mean we ditch all the other syntax?  Not
 necessarily - it's just that __traits can be the _backend_ for many
 other features.

 First of all __traits' name has to be revised.  The double-underscore
 just isn't working for me.  I think it would be a fair tradeoff to
 rename it "traits" and rename the std.traits and core.traits modules
 something else.  Come on Walter, I know that adding keywords is
 undesirable, but ffs, at some point _not_ adding keywords is just as
 bad.  D does not have a very large user or codebase, and if you're
 going to break backwards compatibility - _break it now_, before it's
 too late.  (besides, some of the keyword mass will need to be
 redistributed when imaginary/complex types are removed ;) )

 Secondly - the type properties are cute but they're not very flexible.
  They can interfere with fields and methods, and so the compiler has
 to explicitly check that aggregate member names don't step on the
 built-in property names.  I think that "T.prop" could just be replaced
 with "traits(prop, T)".  traits(min, int), and so on.  Yes, it's
 longer - but that's what templates are for, if you really want it
 shorter: Min!(int).

 Third, the is() expression is greatly overstepping its bounds now that
 traits is around.  Why is there "is(T == class)", but
 __traits(isAssociativeArray, T)?  The is(T) form can be replaced by
 __traits(compiles).  The is(T : U) form can be replaced by
 traits(convertible).  The is(T == U) forms can all be replaced by
 traits(equivalent) and traits(isClass) and the like.  The strange is(T
 U), is(T U : V), is(T U == V) forms.. I'm not sure what to do about
 those.  The ones like is(T U == return) are an obvious abuse and
 should be replaced with traits(returnType) or the like.

 The fourth and final issue is template specialization.  This one
 really does have too much inertia to remove.  So what I propose for
 this is that is() -- now that it has the ability to perform just about
 everything that template specialization can -- should become the
 backend for template specializations.  That is, something like:

 template Foo(T, U : V[K], K, V)
 {
 ...
 }

 is just a shorter way of writing something like:

 template Foo(T, U) if(is(U : V[K]))
 {
 ...
 }

 I don't think template constraints currently introduce symbols into
 the template body, but this would declare V and K as types within the
 body of Foo.

 But here's the kicker: even is() is not an entirely basic construct.
 It's more or less a shortcut for more complex traits expressions
 combined with the ability to alias traits to symbols.  That is, "is(U
 : V[K])" is like the pseudocode "traits(isAssociativeArray, U) &&
 alias traits(itemType, U) V && alias traits(indexType, U) K".  Of
 course you can't put aliases in expressions, but the overall idea is
 that this is() expression is the same as using isAssociativeArray and
 then aliasing other traits as K and V.

 Why do this?  Simplicity, generality, and consistency.  Once all this
 is done, it becomes easy to see what questions can and can't be asked
 about your code.  You only have to look in one place: traits.
 Everything else is defined in terms of it.

 ...

 So what do you think?

I think neither __traits nor a property is good enough for compile-time reflection. I think just one property is enough. For example in Java you do: someInstance.getClass() and then you enter "the reflection world", which uses the same language as Java, but it's at a different level. So: var.reflect or something like that would be awesome. Then you can do: - something.reflect.methods - something.reflect.isVirtual - something.reflect.isAbstract - something.reflect() // same as something.reflect.compileTimeValue - something.reflect.fields - etc. So you just don't allow "reflect" (or whatever) as a field name (if you define it, it's an error, much like "sizeof"), but once you enter "reflect" the compiler can add as many name as it wants, nobody can override these. "reflect" is smart so that for an expression, it return a specific (compile-time) type; for classes, another (compile-time) type; for variables, another (compile-time) type; etc.

Or you could just call it traits. something.traits.methods something.traits.max something.traits.sizeof (1+34.).traits.typeof I do like the general idea of unifying this stuff. Can you make an alias or variable of this .traits/.reflect type? Can it return a metaclass type of some sort so that an alias would be possible? I.e. alias t = something.traits; --bb

I think that's an alias of an expression, and you can't do that. I was thinking maybe something.traits returns an instance of an object that is very well defined, but is only available at compile-time. So: class C { } ClassTraits ct = C.traits; MethodTrait[] methods = ct.methods; etc. ClassTraits can be something like a struct, or a class, or interface, it doesn't matter, it doesn't have a run-time implementation, but it's defined in some std.traits module, but is "special" (the compiler knows what it is, so it can treat it differently). Then you can defined functions over this traits: bool hasFourMethods(ClassTraits ct) { return ct.methods.length == 4; } Since these are defined somewhere in the source code, they can participate in autocompletion, and make it much easier to write generic retrospective code. But maybe this is too much... :-P My idea is to use the same syntax for compile-time and run-time, so the user just has to learn one syntax. The user should know which things are compile-time only, and the compiler will help him by saying "No, I can't do that, you are treating this as run-time but it's only compile-time".
Nov 25 2008
parent Christopher Wright <dhasenan gmail.com> writes:
Ary Borenszweig wrote:
 I think that's an alias of an expression, and you can't do that. I was 
 thinking maybe something.traits returns an instance of an object that is 
 very well defined, but is only available at compile-time. So:

If Walter puts forth the effort to create this, very little further effort would make it available at runtime, most likely. It'd be extremely convenient, though.
Nov 25 2008
prev sibling next sibling parent reply "Bill Baxter" <wbaxter gmail.com> writes:
On Tue, Nov 25, 2008 at 11:48 AM, Ary Borenszweig <ary esperanto.org.ar> wr=
ote:
 Jarrett Billingsley escribi=F3:
 Once upon a time, D did not have string mixins, or CTFE, or templates,
 or any of the fun things we have today.  Even so, it was still
 important to be able to access some information about types.  So
 Walter made it possible to query some basic information - the
 initialization value, minimum and maximum allowable values, byte size
 and alignment etc. - directly from the types as properties.  Neat.

 Then D got templates.  D's templates were based on C++'s, to an
 extent, and therefore made use of specialization (and by corollary,
 SFINAE) to determine which template to instantiate.  By their nature,
 templates are a sort of way of introspecting types.  So now D has two
 ways to find out things about types.  Okay.

 It turned out that templates were not always powerful enough - or
 sufficiently concise - to express some ideas.  So Walter came up with
 the is() expression (originally the "iftype" statement which begat
 "is()" and "static if") to query other, more interesting information
 about types.  It's _kind of_ like template specialization but has some
 extra features that specialization doesn't.  Now D has three ways to
 find out things about types.  Hm.

 Along comes D2, and with it, the __traits keyword.  __traits is
 wonderful (except for the double-underscore name, anyway).  It's
 extensible, flexible, and can answer queries about types and other
 program objects in a way that would be extremely convoluted or cryptic
 if templates or the is() expression were extended.

 But now D programs have _four_ ways to ask questions about themselves.

 Some of these methods overlap but with wildly different syntax.  Many
 questions have to be composed out of these four disparate methods in
 unintuitive manners.  Walter's "Templates Revisited" article says that
 "[m]any useful aspects of C++ templates have been discovered rather
 than designed," but in all honesty, this is exactly the situation with
 D's compile-time introspection.  D's templates are better, yes, but
 the problem has simply been promoted to a wider scope.

 So what can we do?  I've been thinking about it and I think that
 __traits, coupled with the new template constraints, can handle just
 about everything.  Does that mean we ditch all the other syntax?  Not
 necessarily - it's just that __traits can be the _backend_ for many
 other features.

 First of all __traits' name has to be revised.  The double-underscore
 just isn't working for me.  I think it would be a fair tradeoff to
 rename it "traits" and rename the std.traits and core.traits modules
 something else.  Come on Walter, I know that adding keywords is
 undesirable, but ffs, at some point _not_ adding keywords is just as
 bad.  D does not have a very large user or codebase, and if you're
 going to break backwards compatibility - _break it now_, before it's
 too late.  (besides, some of the keyword mass will need to be
 redistributed when imaginary/complex types are removed ;) )

 Secondly - the type properties are cute but they're not very flexible.
  They can interfere with fields and methods, and so the compiler has
 to explicitly check that aggregate member names don't step on the
 built-in property names.  I think that "T.prop" could just be replaced
 with "traits(prop, T)".  traits(min, int), and so on.  Yes, it's
 longer - but that's what templates are for, if you really want it
 shorter: Min!(int).

 Third, the is() expression is greatly overstepping its bounds now that
 traits is around.  Why is there "is(T =3D=3D class)", but
 __traits(isAssociativeArray, T)?  The is(T) form can be replaced by
 __traits(compiles).  The is(T : U) form can be replaced by
 traits(convertible).  The is(T =3D=3D U) forms can all be replaced by
 traits(equivalent) and traits(isClass) and the like.  The strange is(T
 U), is(T U : V), is(T U =3D=3D V) forms.. I'm not sure what to do about
 those.  The ones like is(T U =3D=3D return) are an obvious abuse and
 should be replaced with traits(returnType) or the like.

 The fourth and final issue is template specialization.  This one
 really does have too much inertia to remove.  So what I propose for
 this is that is() -- now that it has the ability to perform just about
 everything that template specialization can -- should become the
 backend for template specializations.  That is, something like:

 template Foo(T, U : V[K], K, V)
 {
 ...
 }

 is just a shorter way of writing something like:

 template Foo(T, U) if(is(U : V[K]))
 {
 ...
 }

 I don't think template constraints currently introduce symbols into
 the template body, but this would declare V and K as types within the
 body of Foo.

 But here's the kicker: even is() is not an entirely basic construct.
 It's more or less a shortcut for more complex traits expressions
 combined with the ability to alias traits to symbols.  That is, "is(U
 : V[K])" is like the pseudocode "traits(isAssociativeArray, U) &&
 alias traits(itemType, U) V && alias traits(indexType, U) K".  Of
 course you can't put aliases in expressions, but the overall idea is
 that this is() expression is the same as using isAssociativeArray and
 then aliasing other traits as K and V.

 Why do this?  Simplicity, generality, and consistency.  Once all this
 is done, it becomes easy to see what questions can and can't be asked
 about your code.  You only have to look in one place: traits.
 Everything else is defined in terms of it.

 ...

 So what do you think?

I liked the way you wrote this. :-) I think neither __traits nor a property is good enough for compile-time reflection. I think just one property is enough. For example in Java you do: someInstance.getClass() and then you enter "the reflection world", which uses the same language a=

 Java, but it's at a different level.

 So:

 var.reflect

 or something like that would be awesome. Then you can do:

 - something.reflect.methods
 - something.reflect.isVirtual
 - something.reflect.isAbstract
 - something.reflect() // same as something.reflect.compileTimeValue
 - something.reflect.fields
 - etc.

 So you just don't allow "reflect" (or whatever) as a field name (if you
 define it, it's an error, much like "sizeof"), but once you enter "reflec=

 the compiler can add as many name as it wants, nobody can override these.
 "reflect" is smart so that for an expression, it return a specific
 (compile-time) type; for classes, another (compile-time) type; for
 variables, another (compile-time) type; etc.

Or you could just call it traits. something.traits.methods something.traits.max something.traits.sizeof (1+34.).traits.typeof I do like the general idea of unifying this stuff. Can you make an alias or variable of this .traits/.reflect type? Can it return a metaclass type of some sort so that an alias would be possible? I.e. alias t =3D something.traits; --bb
Nov 24 2008
parent "Nick Sabalausky" <a a.a> writes:
"Bill Baxter" <wbaxter gmail.com> wrote in message 
news:mailman.54.1227583827.22690.digitalmars-d puremagic.com...
On Tue, Nov 25, 2008 at 11:48 AM, Ary Borenszweig <ary esperanto.org.ar> 
wrote:
 I liked the way you wrote this. :-)

 I think neither __traits nor a property is good enough for compile-time
 reflection. I think just one property is enough.

 For example in Java you do:

 someInstance.getClass()

 and then you enter "the reflection world", which uses the same language 
 as
 Java, but it's at a different level.

 So:

 var.reflect

 or something like that would be awesome. Then you can do:

 - something.reflect.methods
 - something.reflect.isVirtual
 - something.reflect.isAbstract
 - something.reflect() // same as something.reflect.compileTimeValue
 - something.reflect.fields
 - etc.

 So you just don't allow "reflect" (or whatever) as a field name (if you
 define it, it's an error, much like "sizeof"), but once you enter 
 "reflect"
 the compiler can add as many name as it wants, nobody can override these.
 "reflect" is smart so that for an expression, it return a specific
 (compile-time) type; for classes, another (compile-time) type; for
 variables, another (compile-time) type; etc.

Or you could just call it traits. something.traits.methods something.traits.max something.traits.sizeof (1+34.).traits.typeof I do like the general idea of unifying this stuff. Can you make an alias or variable of this .traits/.reflect type? Can it return a metaclass type of some sort so that an alias would be possible? I.e. alias t = something.traits;

That would make it much easier to port over Java code that uses reflection. And even aside from that, I think it's a very nice and clean solution.
Nov 25 2008
prev sibling next sibling parent reply Don <nospam nospam.com> writes:
Jarrett Billingsley wrote:
 Once upon a time, D did not have string mixins, or CTFE, or templates,
 or any of the fun things we have today.  Even so, it was still
 important to be able to access some information about types.  So
 Walter made it possible to query some basic information - the
 initialization value, minimum and maximum allowable values, byte size
 and alignment etc. - directly from the types as properties.  Neat.
 
 Then D got templates.  D's templates were based on C++'s, to an
 extent, and therefore made use of specialization (and by corollary,
 SFINAE) to determine which template to instantiate.  By their nature,
 templates are a sort of way of introspecting types.  So now D has two
 ways to find out things about types.  Okay.
 
 It turned out that templates were not always powerful enough - or
 sufficiently concise - to express some ideas.  So Walter came up with
 the is() expression (originally the "iftype" statement which begat
 "is()" and "static if") to query other, more interesting information
 about types.  It's _kind of_ like template specialization but has some
 extra features that specialization doesn't.  Now D has three ways to
 find out things about types.  Hm.
 
 Along comes D2, and with it, the __traits keyword.  __traits is
 wonderful (except for the double-underscore name, anyway).  It's
 extensible, flexible, and can answer queries about types and other
 program objects in a way that would be extremely convoluted or cryptic
 if templates or the is() expression were extended.
 
 But now D programs have _four_ ways to ask questions about themselves.

 Some of these methods overlap but with wildly different syntax.

You left out typeof/typeinfo/typeid. There's run-time typeinfo, as well as compile-time. This is just begging for unification. Consider that in a CTFE function, you only use run-time syntax, but it's actually occuring at compile time. And since Object now contains a factory method, there's considerable potential for deep unification.
Nov 25 2008
next sibling parent Kagamin <spam here.lot> writes:
Don Wrote:

  > Some of these methods overlap but with wildly different syntax.
 
 You left out typeof/typeinfo/typeid. There's run-time typeinfo, as well 
 as compile-time.

I'd like real uniform API for both rtti and ctti too (ctti code being subject to compile time evaluation). Template constraints could be expressed as contracts. All this declarative syntax can get very messy and unintuitive.
Nov 25 2008
prev sibling parent Lars Ivar Igesund <larsivar igesund.net> writes:
Don wrote:

 And since Object now contains a factory method, there's considerable
 potential for deep unification.

Except that factory is a very bad name to use in D. Considering D's property syntax, factory will imply that you get an instance of some factory. Methods in D doing something should be verbs, and this is even more important for official API's. I suggest create. Although I don't agree with putting a wrapper for ClassInfo.create in Object just for the sake of it, in the first place :P -- Lars Ivar Igesund blog at http://larsivi.net DSource, #d.tango & #D: larsivi Dancing the Tango
Nov 25 2008
prev sibling next sibling parent reply Aarti_pl <aarti interia.pl> writes:
Jarrett Billingsley pisze:
 Once upon a time, D did not have string mixins, or CTFE, or templates,
 or any of the fun things we have today.  Even so, it was still
 important to be able to access some information about types.  So
 Walter made it possible to query some basic information - the
 initialization value, minimum and maximum allowable values, byte size
 and alignment etc. - directly from the types as properties.  Neat.
 
 Then D got templates.  D's templates were based on C++'s, to an
 extent, and therefore made use of specialization (and by corollary,
 SFINAE) to determine which template to instantiate.  By their nature,
 templates are a sort of way of introspecting types.  So now D has two
 ways to find out things about types.  Okay.
 
 It turned out that templates were not always powerful enough - or
 sufficiently concise - to express some ideas.  So Walter came up with
 the is() expression (originally the "iftype" statement which begat
 "is()" and "static if") to query other, more interesting information
 about types.  It's _kind of_ like template specialization but has some
 extra features that specialization doesn't.  Now D has three ways to
 find out things about types.  Hm.
 
 Along comes D2, and with it, the __traits keyword.  __traits is
 wonderful (except for the double-underscore name, anyway).  It's
 extensible, flexible, and can answer queries about types and other
 program objects in a way that would be extremely convoluted or cryptic
 if templates or the is() expression were extended.
 
 But now D programs have _four_ ways to ask questions about themselves.
 
 Some of these methods overlap but with wildly different syntax.  Many
 questions have to be composed out of these four disparate methods in
 unintuitive manners.  Walter's "Templates Revisited" article says that
 "[m]any useful aspects of C++ templates have been discovered rather
 than designed," but in all honesty, this is exactly the situation with
 D's compile-time introspection.  D's templates are better, yes, but
 the problem has simply been promoted to a wider scope.
 
 So what can we do?  I've been thinking about it and I think that
 __traits, coupled with the new template constraints, can handle just
 about everything.  Does that mean we ditch all the other syntax?  Not
 necessarily - it's just that __traits can be the _backend_ for many
 other features.
 
 First of all __traits' name has to be revised.  The double-underscore
 just isn't working for me.  I think it would be a fair tradeoff to
 rename it "traits" and rename the std.traits and core.traits modules
 something else.  Come on Walter, I know that adding keywords is
 undesirable, but ffs, at some point _not_ adding keywords is just as
 bad.  D does not have a very large user or codebase, and if you're
 going to break backwards compatibility - _break it now_, before it's
 too late.  (besides, some of the keyword mass will need to be
 redistributed when imaginary/complex types are removed ;) )
 
 Secondly - the type properties are cute but they're not very flexible.
  They can interfere with fields and methods, and so the compiler has
 to explicitly check that aggregate member names don't step on the
 built-in property names.  I think that "T.prop" could just be replaced
 with "traits(prop, T)".  traits(min, int), and so on.  Yes, it's
 longer - but that's what templates are for, if you really want it
 shorter: Min!(int).
 
 Third, the is() expression is greatly overstepping its bounds now that
 traits is around.  Why is there "is(T == class)", but
 __traits(isAssociativeArray, T)?  The is(T) form can be replaced by
 __traits(compiles).  The is(T : U) form can be replaced by
 traits(convertible).  The is(T == U) forms can all be replaced by
 traits(equivalent) and traits(isClass) and the like.  The strange is(T
 U), is(T U : V), is(T U == V) forms.. I'm not sure what to do about
 those.  The ones like is(T U == return) are an obvious abuse and
 should be replaced with traits(returnType) or the like.
 
 The fourth and final issue is template specialization.  This one
 really does have too much inertia to remove.  So what I propose for
 this is that is() -- now that it has the ability to perform just about
 everything that template specialization can -- should become the
 backend for template specializations.  That is, something like:
 
 template Foo(T, U : V[K], K, V)
 {
 ...
 }
 
 is just a shorter way of writing something like:
 
 template Foo(T, U) if(is(U : V[K]))
 {
 ...
 }
 
 I don't think template constraints currently introduce symbols into
 the template body, but this would declare V and K as types within the
 body of Foo.
 
 But here's the kicker: even is() is not an entirely basic construct.
 It's more or less a shortcut for more complex traits expressions
 combined with the ability to alias traits to symbols.  That is, "is(U
 : V[K])" is like the pseudocode "traits(isAssociativeArray, U) &&
 alias traits(itemType, U) V && alias traits(indexType, U) K".  Of
 course you can't put aliases in expressions, but the overall idea is
 that this is() expression is the same as using isAssociativeArray and
 then aliasing other traits as K and V.
 
 Why do this?  Simplicity, generality, and consistency.  Once all this
 is done, it becomes easy to see what questions can and can't be asked
 about your code.  You only have to look in one place: traits.
 Everything else is defined in terms of it.
 
 ...
 
 So what do you think?

I completely agree that compile time introspection in D is very messy. There are some other problems with CT introspection, which I have explained in my post some time ago: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=77654 In the same post I also proposed unified syntax for template specialization, static if, static assert and alias, while dropping completely is() expression for templates. Basically my proposal is about extending template pattern matching. You have touched few other areas which needs to be rethought. But I think that just using traits will not work very good. It will be too explicit e.g. Your proposal: static if(is(traits(isAssociativeArray, T))) { traits(associativeArrayKeyType, K); traits(associativeArrayValueType, V); } Compared to my proposal: static if (T V K : V[K]) { //V and K is already defined here } Anyway merging these two proposals will improve situation significantly. BTW. I also hate underscores in keywords. :-P Regards Marcin Kuszczak (aarti_pl)
Nov 25 2008
next sibling parent Marcin Kuszczak <aarti_please_no spam_interia.pl> writes:
Jarrett Billingsley wrote:

 On Tue, Nov 25, 2008 at 3:56 AM, Aarti_pl <aarti interia.pl> wrote:
 I completely agree that compile time introspection in D is very messy.

 There are some other problems with CT introspection, which I have
 explained in my post some time ago:
 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=77654

 In the same post I also proposed unified syntax for template
 specialization, static if, static assert and alias, while dropping
 completely is() expression for templates. Basically my proposal is about
 extending template pattern matching.

 You have touched few other areas which needs to be rethought. But I think
 that just using traits will not work very good. It will be too explicit
 e.g.

 Your proposal:
 static if(is(traits(isAssociativeArray, T))) {
  traits(associativeArrayKeyType, K);
  traits(associativeArrayValueType, V);
 }

 Compared to my proposal:
 static if (T V K : V[K]) {
  //V and K is already defined here
 }

 Anyway merging these two proposals will improve situation significantly.

Ahh, nonono, I proposed keeping is() but only as syntactic sugar for traits() magic. So under my proposal one would be able to do: static if(is(T : V[K], K, V)) { // V and K are defined here }

You are the second victim of strange D is expression syntax. If it does help you the first was probably Andrei on this newsgroup :-) (http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=77727) Above, given by you, example is incorrect and does not compile. The proper syntax for querying of types for associative arrays is, so strange that probably nobody can use it without looking into documentation. Below is proper syntax, which works with DMD 2.021: Please notice that 'V' is before '==', but 'K' is after pattern matching expression (sic!). One type is used before declaration, but the second one after ! void main() { alias long[char[]] T; static if (is(T V == V[K], K)) { pragma(msg, V.stringof ~ " " ~ K.stringof); } else static if (is(T V == V[])) { pragma(msg, V.stringof); } }
 Except that it would just be _implemented_ using traits.  :)

 BTW. I also hate underscores in keywords. :-P

They are the devil!

Please notice that my proposal is not directly connected with your's, but anyway it still concerns the same subject: D templates syntax should be rethought and redesigned, because currently there is a lot of mess. (Yes, yes - I know that it is still much better than in C++ :-)) ). -- Regards Marcin Kuszczak (Aarti_pl) ------------------------------------- Ask me why I believe in Jesus - http://www.zapytajmnie.com (en/pl) Doost (port of few Boost libraries) - http://www.dsource.org/projects/doost/ -------------------------------------
Nov 25 2008
prev sibling parent Marcin Kuszczak <aarti_please_no spam_interia.pl> writes:
Nick Sabalausky wrote:

 
 "Jarrett Billingsley" <jarrett.billingsley gmail.com> wrote in message
 Ahh, nonono, I proposed keeping is() but only as syntactic sugar for
 traits() magic.  So under my proposal one would be able to do:

 static if(is(T : V[K], K, V))
 {
   // V and K are defined here
 }

I think his "eliminate is()" proposal was about turning the above into something like this: static if(T : V[K], K, V) instead of eliminating what is() does.

Almost exactly like you said, but also with reverted position of introduced types, so that declaration will be before usage. So instead of *current* syntax (in Jarret's example is syntax error): static if(is(T V : V[K], K)) you could just use: static if(T V K : V[K]) Looks simpler to me :-) -- Regards Marcin Kuszczak (Aarti_pl) ------------------------------------- Ask me why I believe in Jesus - http://www.zapytajmnie.com (en/pl) Doost (port of few Boost libraries) - http://www.dsource.org/projects/doost/ -------------------------------------
Nov 25 2008
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Tue, 25 Nov 2008 15:11:38 +0300, Ary Borenszweig <ary esperanto.org.ar>  
wrote:

 Bill Baxter wrote:
 On Tue, Nov 25, 2008 at 11:48 AM, Ary Borenszweig  
 <ary esperanto.org.ar> wrote:
 Jarrett Billingsley escribió:
 Once upon a time, D did not have string mixins, or CTFE, or templates,
 or any of the fun things we have today.  Even so, it was still
 important to be able to access some information about types.  So
 Walter made it possible to query some basic information - the
 initialization value, minimum and maximum allowable values, byte size
 and alignment etc. - directly from the types as properties.  Neat.

 Then D got templates.  D's templates were based on C++'s, to an
 extent, and therefore made use of specialization (and by corollary,
 SFINAE) to determine which template to instantiate.  By their nature,
 templates are a sort of way of introspecting types.  So now D has two
 ways to find out things about types.  Okay.

 It turned out that templates were not always powerful enough - or
 sufficiently concise - to express some ideas.  So Walter came up with
 the is() expression (originally the "iftype" statement which begat
 "is()" and "static if") to query other, more interesting information
 about types.  It's _kind of_ like template specialization but has some
 extra features that specialization doesn't.  Now D has three ways to
 find out things about types.  Hm.

 Along comes D2, and with it, the __traits keyword.  __traits is
 wonderful (except for the double-underscore name, anyway).  It's
 extensible, flexible, and can answer queries about types and other
 program objects in a way that would be extremely convoluted or cryptic
 if templates or the is() expression were extended.

 But now D programs have _four_ ways to ask questions about themselves.

 Some of these methods overlap but with wildly different syntax.  Many
 questions have to be composed out of these four disparate methods in
 unintuitive manners.  Walter's "Templates Revisited" article says that
 "[m]any useful aspects of C++ templates have been discovered rather
 than designed," but in all honesty, this is exactly the situation with
 D's compile-time introspection.  D's templates are better, yes, but
 the problem has simply been promoted to a wider scope.

 So what can we do?  I've been thinking about it and I think that
 __traits, coupled with the new template constraints, can handle just
 about everything.  Does that mean we ditch all the other syntax?  Not
 necessarily - it's just that __traits can be the _backend_ for many
 other features.

 First of all __traits' name has to be revised.  The double-underscore
 just isn't working for me.  I think it would be a fair tradeoff to
 rename it "traits" and rename the std.traits and core.traits modules
 something else.  Come on Walter, I know that adding keywords is
 undesirable, but ffs, at some point _not_ adding keywords is just as
 bad.  D does not have a very large user or codebase, and if you're
 going to break backwards compatibility - _break it now_, before it's
 too late.  (besides, some of the keyword mass will need to be
 redistributed when imaginary/complex types are removed ;) )

 Secondly - the type properties are cute but they're not very flexible.
  They can interfere with fields and methods, and so the compiler has
 to explicitly check that aggregate member names don't step on the
 built-in property names.  I think that "T.prop" could just be replaced
 with "traits(prop, T)".  traits(min, int), and so on.  Yes, it's
 longer - but that's what templates are for, if you really want it
 shorter: Min!(int).

 Third, the is() expression is greatly overstepping its bounds now that
 traits is around.  Why is there "is(T == class)", but
 __traits(isAssociativeArray, T)?  The is(T) form can be replaced by
 __traits(compiles).  The is(T : U) form can be replaced by
 traits(convertible).  The is(T == U) forms can all be replaced by
 traits(equivalent) and traits(isClass) and the like.  The strange is(T
 U), is(T U : V), is(T U == V) forms.. I'm not sure what to do about
 those.  The ones like is(T U == return) are an obvious abuse and
 should be replaced with traits(returnType) or the like.

 The fourth and final issue is template specialization.  This one
 really does have too much inertia to remove.  So what I propose for
 this is that is() -- now that it has the ability to perform just about
 everything that template specialization can -- should become the
 backend for template specializations.  That is, something like:

 template Foo(T, U : V[K], K, V)
 {
 ...
 }

 is just a shorter way of writing something like:

 template Foo(T, U) if(is(U : V[K]))
 {
 ...
 }

 I don't think template constraints currently introduce symbols into
 the template body, but this would declare V and K as types within the
 body of Foo.

 But here's the kicker: even is() is not an entirely basic construct.
 It's more or less a shortcut for more complex traits expressions
 combined with the ability to alias traits to symbols.  That is, "is(U
 : V[K])" is like the pseudocode "traits(isAssociativeArray, U) &&
 alias traits(itemType, U) V && alias traits(indexType, U) K".  Of
 course you can't put aliases in expressions, but the overall idea is
 that this is() expression is the same as using isAssociativeArray and
 then aliasing other traits as K and V.

 Why do this?  Simplicity, generality, and consistency.  Once all this
 is done, it becomes easy to see what questions can and can't be asked
 about your code.  You only have to look in one place: traits.
 Everything else is defined in terms of it.

 ...

 So what do you think?

I think neither __traits nor a property is good enough for compile-time reflection. I think just one property is enough. For example in Java you do: someInstance.getClass() and then you enter "the reflection world", which uses the same language as Java, but it's at a different level. So: var.reflect or something like that would be awesome. Then you can do: - something.reflect.methods - something.reflect.isVirtual - something.reflect.isAbstract - something.reflect() // same as something.reflect.compileTimeValue - something.reflect.fields - etc. So you just don't allow "reflect" (or whatever) as a field name (if you define it, it's an error, much like "sizeof"), but once you enter "reflect" the compiler can add as many name as it wants, nobody can override these. "reflect" is smart so that for an expression, it return a specific (compile-time) type; for classes, another (compile-time) type; for variables, another (compile-time) type; etc.

something.traits.methods something.traits.max something.traits.sizeof (1+34.).traits.typeof I do like the general idea of unifying this stuff. Can you make an alias or variable of this .traits/.reflect type? Can it return a metaclass type of some sort so that an alias would be possible? I.e. alias t = something.traits; --bb

I think that's an alias of an expression, and you can't do that. I was thinking maybe something.traits returns an instance of an object that is very well defined, but is only available at compile-time. So: class C { } ClassTraits ct = C.traits; MethodTrait[] methods = ct.methods; etc. ClassTraits can be something like a struct, or a class, or interface, it doesn't matter, it doesn't have a run-time implementation, but it's defined in some std.traits module, but is "special" (the compiler knows what it is, so it can treat it differently). Then you can defined functions over this traits: bool hasFourMethods(ClassTraits ct) { return ct.methods.length == 4; } Since these are defined somewhere in the source code, they can participate in autocompletion, and make it much easier to write generic retrospective code. But maybe this is too much... :-P My idea is to use the same syntax for compile-time and run-time, so the user just has to learn one syntax. The user should know which things are compile-time only, and the compiler will help him by saying "No, I can't do that, you are treating this as run-time but it's only compile-time".

I agree, that's what it should always have been. object.traits could be an instance of (std.typecons.)Tuple rather that some artificial inner struct. In either way, foreach over its elements should be available at CT.
Nov 25 2008
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
Jarrett,

I agree with most of what you are saying, except for this:

"Jarrett Billingsley" wrote
 Secondly - the type properties are cute but they're not very flexible.
 They can interfere with fields and methods, and so the compiler has
 to explicitly check that aggregate member names don't step on the
 built-in property names.  I think that "T.prop" could just be replaced
 with "traits(prop, T)".  traits(min, int), and so on.  Yes, it's
 longer - but that's what templates are for, if you really want it
 shorter: Min!(int).

Ugh! Can we just change traits to not use the functional style? I like as others have suggested: C.traits.isVirtualMethod(foo); substitute traits for your favorite reflection keyword (how about traitsof?) There is also one other benefit to min and max (and others) being first class properties. You can mimic their behavior in user-defined types. For example, if int.min is changed to traits(min, int) or even int.traits.min, then how do I define a similar 'minimum' for say, a time type, which would be totally arbitrary. According to the spec, the only builtin properties that user defined types have are init, sizeof, alignof, mangleof, stringof. With the exception of init, most of these are pretty uncommon member names. I wouldn't mind moving init into traits (with the above syntax): C.traits.init The other properties are specific to the builtin types, and therefore cannot conflict with members. -Steve
Nov 25 2008
prev sibling next sibling parent "Jarrett Billingsley" <jarrett.billingsley gmail.com> writes:
On Tue, Nov 25, 2008 at 8:42 AM, Steven Schveighoffer
<schveiguy yahoo.com> wrote:
 Jarrett,

 I agree with most of what you are saying, except for this:

 "Jarrett Billingsley" wrote
 Secondly - the type properties are cute but they're not very flexible.
 They can interfere with fields and methods, and so the compiler has
 to explicitly check that aggregate member names don't step on the
 built-in property names.  I think that "T.prop" could just be replaced
 with "traits(prop, T)".  traits(min, int), and so on.  Yes, it's
 longer - but that's what templates are for, if you really want it
 shorter: Min!(int).

Ugh! Can we just change traits to not use the functional style? I like as others have suggested: C.traits.isVirtualMethod(foo); substitute traits for your favorite reflection keyword (how about traitsof?) There is also one other benefit to min and max (and others) being first class properties. You can mimic their behavior in user-defined types. For example, if int.min is changed to traits(min, int) or even int.traits.min, then how do I define a similar 'minimum' for say, a time type, which would be totally arbitrary. According to the spec, the only builtin properties that user defined types have are init, sizeof, alignof, mangleof, stringof. With the exception of init, most of these are pretty uncommon member names. I wouldn't mind moving init into traits (with the above syntax): C.traits.init The other properties are specific to the builtin types, and therefore cannot conflict with members.

Fair enough. Also, I don't necessarily care about the syntax, just as long as it's unified ;)
Nov 25 2008
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Tue, 25 Nov 2008 16:42:55 +0300, Steven Schveighoffer  
<schveiguy yahoo.com> wrote:

 Jarrett,

 I agree with most of what you are saying, except for this:

 "Jarrett Billingsley" wrote
 Secondly - the type properties are cute but they're not very flexible.
 They can interfere with fields and methods, and so the compiler has
 to explicitly check that aggregate member names don't step on the
 built-in property names.  I think that "T.prop" could just be replaced
 with "traits(prop, T)".  traits(min, int), and so on.  Yes, it's
 longer - but that's what templates are for, if you really want it
 shorter: Min!(int).

Ugh! Can we just change traits to not use the functional style? I like as others have suggested: C.traits.isVirtualMethod(foo);

What is foo in this context? If it is a function (C.foo) then I like "C.foo.traits.isVirtual" better, i.e. each member has its own set of traits: class C { int i; double d; void foo() {} } auto si = C.i.traits.offsetof; auto di = C.d.traits.init; auto fv = C.foo.traits.isVirtual; auto fv2 = C.traits.methods[0].isVirtual;
Nov 25 2008
prev sibling next sibling parent reply "Jarrett Billingsley" <jarrett.billingsley gmail.com> writes:
On Tue, Nov 25, 2008 at 3:56 AM, Aarti_pl <aarti interia.pl> wrote:
 I completely agree that compile time introspection in D is very messy.

 There are some other problems with CT introspection, which I have explained
 in my post some time ago:
 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=77654

 In the same post I also proposed unified syntax for template specialization,
 static if, static assert and alias, while dropping completely is()
 expression for templates. Basically my proposal is about extending template
 pattern matching.

 You have touched few other areas which needs to be rethought. But I think
 that just using traits will not work very good. It will be too explicit e.g.

 Your proposal:
 static if(is(traits(isAssociativeArray, T))) {
  traits(associativeArrayKeyType, K);
  traits(associativeArrayValueType, V);
 }

 Compared to my proposal:
 static if (T V K : V[K]) {
  //V and K is already defined here
 }

 Anyway merging these two proposals will improve situation significantly.

Ahh, nonono, I proposed keeping is() but only as syntactic sugar for traits() magic. So under my proposal one would be able to do: static if(is(T : V[K], K, V)) { // V and K are defined here } Except that it would just be _implemented_ using traits. :)
 BTW. I also hate underscores in keywords. :-P

They are the devil!
Nov 25 2008
parent "Nick Sabalausky" <a a.a> writes:
"Jarrett Billingsley" <jarrett.billingsley gmail.com> wrote in message 
news:mailman.56.1227629961.22690.digitalmars-d puremagic.com...
 On Tue, Nov 25, 2008 at 3:56 AM, Aarti_pl <aarti interia.pl> wrote:
 I completely agree that compile time introspection in D is very messy.

 There are some other problems with CT introspection, which I have 
 explained
 in my post some time ago:
 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=77654

 In the same post I also proposed unified syntax for template 
 specialization,
 static if, static assert and alias, while dropping completely is()
 expression for templates. Basically my proposal is about extending 
 template
 pattern matching.

 You have touched few other areas which needs to be rethought. But I think
 that just using traits will not work very good. It will be too explicit 
 e.g.

 Your proposal:
 static if(is(traits(isAssociativeArray, T))) {
  traits(associativeArrayKeyType, K);
  traits(associativeArrayValueType, V);
 }

 Compared to my proposal:
 static if (T V K : V[K]) {
  //V and K is already defined here
 }

 Anyway merging these two proposals will improve situation significantly.

Ahh, nonono, I proposed keeping is() but only as syntactic sugar for traits() magic. So under my proposal one would be able to do: static if(is(T : V[K], K, V)) { // V and K are defined here }

I think his "eliminate is()" proposal was about turning the above into something like this: static if(T : V[K], K, V) instead of eliminating what is() does.
 Except that it would just be _implemented_ using traits.  :)

 BTW. I also hate underscores in keywords. :-P

They are the devil!

Nov 25 2008
prev sibling parent reply "Bill Baxter" <wbaxter gmail.com> writes:
On Tue, Nov 25, 2008 at 10:42 PM, Steven Schveighoffer
<schveiguy yahoo.com> wrote:
 There is also one other benefit to min and max (and others) being first
 class properties.  You can mimic their behavior in user-defined types.  For
 example, if int.min is changed to traits(min, int) or even int.traits.min,
 then how do I define a similar 'minimum' for say, a time type, which would
 be totally arbitrary.

Agreed. Clearly we need a way to define custom traits info, too. It could be done using some kind of traits{...} block in a class, that contains only functions and members safe for compile use. --bb
Nov 25 2008
parent "Nick Sabalausky" <a a.a> writes:
"Bill Baxter" <wbaxter gmail.com> wrote in message 
news:mailman.57.1227650204.22690.digitalmars-d puremagic.com...
 On Tue, Nov 25, 2008 at 10:42 PM, Steven Schveighoffer
 <schveiguy yahoo.com> wrote:
 There is also one other benefit to min and max (and others) being first
 class properties.  You can mimic their behavior in user-defined types. 
 For
 example, if int.min is changed to traits(min, int) or even 
 int.traits.min,
 then how do I define a similar 'minimum' for say, a time type, which 
 would
 be totally arbitrary.

Agreed. Clearly we need a way to define custom traits info, too. It could be done using some kind of traits{...} block in a class, that contains only functions and members safe for compile use.

[min=4, serializable] class Foo {...}
Nov 25 2008