digitalmars.D - Time for std.reflection
- Andrei Alexandrescu (99/99) Jul 21 2012 Walter and I discussed the idea below a long time (years) ago. Most
- Jonathan M Davis (8/13) Jul 21 2012 Are you proposing that we replace std.traits or that std.reflection be b...
- Andrei Alexandrescu (5/17) Jul 21 2012 On top. std.reflection would be a complete reflection solution, whereas
- Adam Wilson (10/27) Jul 21 2012 I too would want it in a different module But my read of Andrei's propos...
- Michel Fortin (22/23) Jul 21 2012 Unifying runtime and compile-time reflection is a good idea, in theory
- Kapps (14/14) Jul 21 2012 I agree with most things proposed, however I am not a fan of the
- Andrei Alexandrescu (9/22) Jul 21 2012 At no place in the proposed approach is the use of templates required or...
- Kapps (31/61) Jul 21 2012 What I meant was that when something requires reflection, it
- Jonathan M Davis (7/20) Jul 21 2012 Runtime reflection _must_ be opt-in. We do _not_ want to make all types ...
- Kapps (7/17) Jul 21 2012 But the whole point is that not every type has it; only those
- deadalnix (6/10) Jul 22 2012 I don't think both are that related here.
- Jacob Carlborg (4/6) Jul 22 2012 I really hope so.
- Jacob Carlborg (4/9) Jul 22 2012 I agree.
- =?ISO-8859-1?Q?Alex_R=F8nne_Petersen?= (7/106) Jul 21 2012 I'm just curious about one thing: How do you plan to reify templates
- Andrei Alexandrescu (4/6) Jul 21 2012 I don't know.
- deadalnix (3/102) Jul 21 2012 I don't understand. Are theses structures generated by the compiler or
- Andrei Alexandrescu (6/8) Jul 21 2012 These are all library structures. Internally they use std.traits, which
- deadalnix (3/11) Jul 22 2012 It is what happen when you editor collapse the text :D
- Jacob Carlborg (4/6) Jul 22 2012 It's the new hip thing :)
- sclytrack (2/9) Nov 19 2012 Is there a way to get the current module at compile time?
- Adam D. Ruppe (4/4) Nov 19 2012 On Monday, 19 November 2012 at 11:43:36 UTC, sclytrack wrote:
- Andrej Mitrovic (12/25) Jul 21 2012 Are class/struct/function/etc templates going to be stored in the
- Andrei Alexandrescu (6/31) Jul 21 2012 Perhaps we could accommodate parameterized types together with
- Philippe Sigaud (58/71) Jul 22 2012 On Sat, Jul 21, 2012 at 11:44 PM, Andrei Alexandrescu
- Andrei Alexandrescu (28/81) Jul 22 2012 Yah, ideally all entities definable in a D module should be available
- Kagamin (4/8) Jul 22 2012 Plugins are usually done by providing factory methods that create
- Wouter Verhelst (10/17) Jul 22 2012 That's what you'd do in a language that doesn't have something like D's
- Philippe Sigaud (74/118) Jul 22 2012 I wouldn't know. I have no experience with dlsym().
- Dmitry Olshansky (7/11) Jul 22 2012 I kind of did it...
- Philippe Sigaud (6/20) Jul 22 2012 Hey, that was a joke, about implementing a D compiler in D, to be
- Dmitry Olshansky (8/31) Jul 22 2012 I'd advice to wait a bit ... it's not really in a good shape (to read
- Jacob Carlborg (4/8) Jul 22 2012 I see no reason to make it difficult on purpose.
- Andrei Alexandrescu (4/10) Jul 22 2012 The reason is that often the cone would be incomplete and therefore
- David Nadlinger (13/24) Jul 23 2012 You can't do that without breaking the module system – as long
-
Philippe Sigaud
(11/21)
Jul 23 2012
On Mon, Jul 23, 2012 at 2:43 PM, David Nadlinger
wr... - Max Samukha (55/57) Jul 22 2012 There are some questions.
- Philippe Sigaud (16/20) Jul 22 2012 Maybe I don't get your comment, but AFAICT, the language does allow
- Simen Kjaeraas (18/33) Jul 23 2012 Wrong way around. Try this:
- Philippe Sigaud (3/17) Jul 23 2012 Ah, that, OK. And __ctfe does not help.
- Andrej Mitrovic (21/22) Jul 22 2012 I think either way you'd need reference semantics. For example maybe
- Jacob Carlborg (4/15) Jul 22 2012 I've been waiting for this :)
- Philippe Sigaud (10/28) Jul 22 2012 Yeah, I agree, reference semantics look better.
- Jacob Carlborg (5/13) Jul 23 2012 If I recall correctly, Scala uses its reflection API as a part off its
- Manu (5/103) Nov 19 2012 Huge interest!
- Malte Skarupke (23/45) Nov 19 2012 Why do you not use recursion here? Why not
Walter and I discussed the idea below a long time (years) ago. Most likely it's also been discussed in this newsgroup a couple of times. Given the state of the compiler back then, back then it seemed like a super cool idea that's entirely realizable, it would just take time for the compiler to become as capable as needed. Nowadays we're in shape to tackle it. Here "it" is. Back when runtime reflection was being discussed, my response was "let's focus on compile-time reflection, and then we can do run-time reflection on demand as a library". Though this might sound sensible, I initially didn't have a design. Now here's what we can do. Currently we have information about symbols as __traits(...) intrinsics wrapped in nice but scattered ways. Now that CTFE is good enough to manipulate structs and arrays thereof, we have the possibility to finally approach things in a nicely unified, structured way. First, we need to prime std.reflection with a few abstractions that characterize entities in a D program. class ModuleInfo { property: string name(); ImportInfo[] imports(); DataInfo[] data(); FunctionInfo[] functions(); ClassInfo[] classes(); StructInfo[] structs(); // includes unions TemplateInfo[] templates(); EnumInfo[] enums(); bool hasStaticCtor(), hasStaticDtor(), hasSharedCtor(), hasSharedDtor(); } Probably there are a few more pieces of data, but you get the idea. Then for each of the entities mentioned above we have a similar definition. For example: enum Protection { isPublic, isPackage, isProtected, isPrivate } class ClassInfo { property: string name(); string baseName(); string parentName(); // if applicable, null otherwise string[] interfaces(); bool isShared(); Protection protection(); DataMemberInfo[] data(); MethodInfo[] methods(); Object defaultConstructor(); ... } Then for an e.g. method declaration we'd have: class MethodInfo { property: string name(); bool isStatic(), isFinal(), isOverride(); Protection protection(); string[] parameterTypes(); string[] parameterNames(); } Some details may vary, e.g. some may be straight members instead of properties etc. (I used properties to allude to use of lazy gathering of information). So so far we have a nice collection of structured data associated with the entities in a D program. Note how this structuring differs yet has similar power to the primitives in std.traits; std.traits offers unstructured bits of information on demand (e.g. ParameterTypeNames) etc. but the objects above group information together per entity declared. All of the above goes in std.reflection, of course. =========== On to primitives that return such data. Given that D can (since relatively recently) create and manipulate class objects during compilation too, it follows that the classes above can be accessed in two ways - through compile-time API and run-time API. When possible, the APIs may even use the same functions; some other times they will be necessarily different. There are two possible approaches to discovering such information. One is by fetching the ModuleInfo for the whole module and navigating it. Another one is by using search primitives from strings. So we should have e.g. // inside std.reflection ModuleInfo getModuleInfo(string moduleName); so a CT call would go like: // client code static info = getModuleInfo("std.algorithm"); whereas a run-time call would be: // client code auto info = getModuleInfo("std.algorithm"); In the latter case, the module needs to save all needed information for ri, so it should plant this: // inside std.algorithm mixin(makeModuleInfoAvailableDynamically()); The mixin would generate all information needed and would store it for later dynamic use. A search API would go like e.g. ClassInfo getClassInfo(string className); In this case the class name could be qualified with module information etc. =========== With this design we unify compile-time and run-time type manipulation in simple ways, by defining structured information about declarations that can be queried during compilation or dynamically. Please chime in with thoughts. Would someone want to pioneer this project? Andrei
Jul 21 2012
On Saturday, July 21, 2012 17:44:51 Andrei Alexandrescu wrote:With this design we unify compile-time and run-time type manipulation in simple ways, by defining structured information about declarations that can be queried during compilation or dynamically. Please chime in with thoughts.Are you proposing that we replace std.traits or that std.reflection be built on top of it? Having std.reflection will be awesome for a lot of stuff - particularly the more complicated stuff - and would be a great addition to Phobos, but it seems like overkill for a lot of basic template constraints, so I wouldn't want to see it replace std.traits. - Jonathan M Davis
Jul 21 2012
On 7/21/12 5:51 PM, Jonathan M Davis wrote:On Saturday, July 21, 2012 17:44:51 Andrei Alexandrescu wrote:On top. std.reflection would be a complete reflection solution, whereas std.traits gives casual type traits as needed.With this design we unify compile-time and run-time type manipulation in simple ways, by defining structured information about declarations that can be queried during compilation or dynamically. Please chime in with thoughts.Are you proposing that we replace std.traits or that std.reflection be built on top of it?Having std.reflection will be awesome for a lot of stuff - particularly the more complicated stuff - and would be a great addition to Phobos, but it seems like overkill for a lot of basic template constraints, so I wouldn't want to see it replace std.traits.My thoughts exactly. Andrei
Jul 21 2012
On Sat, 21 Jul 2012 14:51:30 -0700, Jonathan M Davis <jmdavisProg gmx.com> wrote:On Saturday, July 21, 2012 17:44:51 Andrei Alexandrescu wrote:I too would want it in a different module But my read of Andrei's proposal is that it would be. -- Adam Wilson IRC: LightBender Project Coordinator The Horizon Project http://www.thehorizonproject.org/With this design we unify compile-time and run-time type manipulation in simple ways, by defining structured information about declarations that can be queried during compilation or dynamically. Please chime in with thoughts.Are you proposing that we replace std.traits or that std.reflection be built on top of it? Having std.reflection will be awesome for a lot of stuff - particularly the more complicated stuff - and would be a great addition to Phobos, but it seems like overkill for a lot of basic template constraints, so I wouldn't want to see it replace std.traits. - Jonathan M Davis
Jul 21 2012
On 2012-07-21 21:44:51 +0000, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> said:Please chime in with thoughts.Unifying runtime and compile-time reflection is a good idea, in theory at least. At least it'd make compile-time reflection easier. But when I think about the use case, unification the way you're proposing doesn't seem that great. If you need runtime reflection in one class, you probably need it on all the derived classes too. That's the real problem runtime reflection solves: it allows you to know something about the subclasses you might otherwise not know about (because your code only know the base class). So to make runtime reflection not a hassle, the thing that generates the runtime reflection info should be inherited somehow. The other thing is: you very rarely need runtime reflection of everything. Especially, why would you ever need runtime reflection for a struct, where all the reflected info is available at compile time? And why create info for all fields and methods in a class in a case where all you need to get at is a pair of serialize/unserialize functions? -- Michel Fortin michel.fortin michelf.ca http://michelf.ca/
Jul 21 2012
I agree with most things proposed, however I am not a fan of the idea of mixing in runtime reflection info. Many times, you want reflection info from a type that is not your own, and thus I believe reflection should be generated by specifying a type. More importantly, a method should exist for recursively generating reflection info. Also, I'd like to see a hierarchal approach to reflection data. The main advantage to std.reflection would be being able to use it at run-time, at which point we can't simply rely on templates, and instead if we want to store something we must rely on a base type. I think the best approach would be a hierarchal system where all reflection data derives from MemberInfo. My ideal API would like something like: https://workflowy.com/shared/62d4f791-e397-a86d-c018-09eab98b9927/
Jul 21 2012
On 7/21/12 8:16 PM, Kapps wrote:I agree with most things proposed, however I am not a fan of the idea of mixing in runtime reflection info. Many times, you want reflection info from a type that is not your own, and thus I believe reflection should be generated by specifying a type. More importantly, a method should exist for recursively generating reflection info.I confess I have trouble understanding each of the sentences above.Also, I'd like to see a hierarchal approach to reflection data. The main advantage to std.reflection would be being able to use it at run-time, at which point we can't simply rely on templates, and instead if we want to store something we must rely on a base type.At no place in the proposed approach is the use of templates required or needed.I think the best approach would be a hierarchal system where all reflection data derives from MemberInfo. My ideal API would like something like: https://workflowy.com/shared/62d4f791-e397-a86d-c018-09eab98b9927/I cringed at MemberType -> { Module, Type, Field, Method, Parameter } I was hoping to get away from slapping tagging on types just for the sake of using inheritance. Andrei
Jul 21 2012
On Sunday, 22 July 2012 at 00:21:31 UTC, Andrei Alexandrescu wrote:On 7/21/12 8:16 PM, Kapps wrote:What I meant was that when something requires reflection, it generally requires that everything it contains has reflection information as well. If using a mixin directly in the module itself, when a module does not include this mixin it will never get reflection information. This may or may not be desired. However it completely prevents many common uses of reflection, including serialization. So long as there is any dependency on any type or method that does not have reflection info, the entire operation would fail. This means that people will either start adding reflection to modules that do not need it in fear that someone may try to use a member of their module, or too sparsely thus making reflection all but unuseable for many common situations.I agree with most things proposed, however I am not a fan of the idea of mixing in runtime reflection info. Many times, you want reflection info from a type that is not your own, and thus I believe reflection should be generated by specifying a type. More importantly, a method should exist for recursively generating reflection info.I confess I have trouble understanding each of the sentences above.What I mean by this is that people rely on the ability to use templates as a replacement to inheritance or common interfaces. For example, ranges rely on methods existing rather than using an interface for a range. With reflection, this isn't ideal. For example, if you wanted to get all the fields and properties in a module, including nested ones, you'd currently have to get all fields, in a module, add those, then get all types in a module, then all types in that type, then get all fields from those, etc. With a hierarchal approach where MemberInfo has a Children property, you'd simply get all children that are a FieldInfo or PropertyInfo, and recurse for all returned values that have children.Also, I'd like to see a hierarchal approach to reflection data. The main advantage to std.reflection would be being able to use it at run-time, at which point we can't simply rely on templates, and instead if we want to store something we must rely on a base type.At no place in the proposed approach is the use of templates required or needed.Admittedly, the MemberType wouldn't be necessary and I don't see as having a huge benefit. In almost all cases you know what you want to operate on, inheritance simply makes the API cleaner and gives access to common methods more easily.I think the best approach would be a hierarchal system where all reflection data derives from MemberInfo. My ideal API would like something like: https://workflowy.com/shared/62d4f791-e397-a86d-c018-09eab98b9927/I cringed at MemberType -> { Module, Type, Field, Method, Parameter } I was hoping to get away from slapping tagging on types just for the sake of using inheritance.
Jul 21 2012
On Sunday, July 22, 2012 05:19:39 Kapps wrote:What I meant was that when something requires reflection, it generally requires that everything it contains has reflection information as well. If using a mixin directly in the module itself, when a module does not include this mixin it will never get reflection information. This may or may not be desired. However it completely prevents many common uses of reflection, including serialization. So long as there is any dependency on any type or method that does not have reflection info, the entire operation would fail. This means that people will either start adding reflection to modules that do not need it in fear that someone may try to use a member of their module, or too sparsely thus making reflection all but unuseable for many common situations.Runtime reflection _must_ be opt-in. We do _not_ want to make all types pay the cost of having it. That means that however it's done is going to require that every type that has it be marked in one way or another to enable that functionality. That's part of the cost of being a systems language. Whatever solution we come up with must take that into account. - Jonathan M Davis
Jul 21 2012
On Sunday, 22 July 2012 at 03:47:16 UTC, Jonathan M Davis wrote:Runtime reflection _must_ be opt-in. We do _not_ want to make all types pay the cost of having it. That means that however it's done is going to require that every type that has it be marked in one way or another to enable that functionality. That's part of the cost of being a systems language. Whatever solution we come up with must take that into account. - Jonathan M DavisBut the whole point is that not every type has it; only those that are registered. It just happens that you can register types besides yourself, which is an absolute necessity for many common purposes of reflection. For serialization for example, the serializer / deserializer should create reflection information for all types contained in a class.
Jul 21 2012
On 22/07/2012 05:46, Jonathan M Davis wrote:Runtime reflection _must_ be opt-in. We do _not_ want to make all types pay the cost of having it. That means that however it's done is going to require that every type that has it be marked in one way or another to enable that functionality.I don't think both are that related here. Yes, we are a system language, so this have to be opt-in. But no, this doesn't means that every type have to be marked to be reflected. I'd expect from std.reflection that it is able to reflect recursively from the marked starting point.
Jul 22 2012
On 2012-07-22 14:04, deadalnix wrote:I'd expect from std.reflection that it is able to reflect recursively from the marked starting point.I really hope so. -- /Jacob Carlborg
Jul 22 2012
On 2012-07-22 02:16, Kapps wrote:I agree with most things proposed, however I am not a fan of the idea of mixing in runtime reflection info. Many times, you want reflection info from a type that is not your own, and thus I believe reflection should be generated by specifying a type. More importantly, a method should exist for recursively generating reflection info.I agree. -- /Jacob Carlborg
Jul 22 2012
On 21-07-2012 23:44, Andrei Alexandrescu wrote:Walter and I discussed the idea below a long time (years) ago. Most likely it's also been discussed in this newsgroup a couple of times. Given the state of the compiler back then, back then it seemed like a super cool idea that's entirely realizable, it would just take time for the compiler to become as capable as needed. Nowadays we're in shape to tackle it. Here "it" is. Back when runtime reflection was being discussed, my response was "let's focus on compile-time reflection, and then we can do run-time reflection on demand as a library". Though this might sound sensible, I initially didn't have a design. Now here's what we can do. Currently we have information about symbols as __traits(...) intrinsics wrapped in nice but scattered ways. Now that CTFE is good enough to manipulate structs and arrays thereof, we have the possibility to finally approach things in a nicely unified, structured way. First, we need to prime std.reflection with a few abstractions that characterize entities in a D program. class ModuleInfo { property: string name(); ImportInfo[] imports(); DataInfo[] data(); FunctionInfo[] functions(); ClassInfo[] classes(); StructInfo[] structs(); // includes unions TemplateInfo[] templates(); EnumInfo[] enums(); bool hasStaticCtor(), hasStaticDtor(), hasSharedCtor(), hasSharedDtor(); } Probably there are a few more pieces of data, but you get the idea. Then for each of the entities mentioned above we have a similar definition. For example: enum Protection { isPublic, isPackage, isProtected, isPrivate } class ClassInfo { property: string name(); string baseName(); string parentName(); // if applicable, null otherwise string[] interfaces(); bool isShared(); Protection protection(); DataMemberInfo[] data(); MethodInfo[] methods(); Object defaultConstructor(); ... } Then for an e.g. method declaration we'd have: class MethodInfo { property: string name(); bool isStatic(), isFinal(), isOverride(); Protection protection(); string[] parameterTypes(); string[] parameterNames(); } Some details may vary, e.g. some may be straight members instead of properties etc. (I used properties to allude to use of lazy gathering of information). So so far we have a nice collection of structured data associated with the entities in a D program. Note how this structuring differs yet has similar power to the primitives in std.traits; std.traits offers unstructured bits of information on demand (e.g. ParameterTypeNames) etc. but the objects above group information together per entity declared. All of the above goes in std.reflection, of course. =========== On to primitives that return such data. Given that D can (since relatively recently) create and manipulate class objects during compilation too, it follows that the classes above can be accessed in two ways - through compile-time API and run-time API. When possible, the APIs may even use the same functions; some other times they will be necessarily different. There are two possible approaches to discovering such information. One is by fetching the ModuleInfo for the whole module and navigating it. Another one is by using search primitives from strings. So we should have e.g. // inside std.reflection ModuleInfo getModuleInfo(string moduleName); so a CT call would go like: // client code static info = getModuleInfo("std.algorithm"); whereas a run-time call would be: // client code auto info = getModuleInfo("std.algorithm"); In the latter case, the module needs to save all needed information for ri, so it should plant this: // inside std.algorithm mixin(makeModuleInfoAvailableDynamically()); The mixin would generate all information needed and would store it for later dynamic use. A search API would go like e.g. ClassInfo getClassInfo(string className); In this case the class name could be qualified with module information etc. =========== With this design we unify compile-time and run-time type manipulation in simple ways, by defining structured information about declarations that can be queried during compilation or dynamically. Please chime in with thoughts. Would someone want to pioneer this project? AndreiI'm just curious about one thing: How do you plan to reify templates (they are Turing complete after all)? -- Alex Rønne Petersen alex lycus.org http://lycus.org
Jul 21 2012
On 7/21/12 8:28 PM, Alex Rønne Petersen wrote:I'm just curious about one thing: How do you plan to reify templates (they are Turing complete after all)?I don't know. Andrei P.S. Please don't overquote.
Jul 21 2012
On 21/07/2012 23:44, Andrei Alexandrescu wrote:Walter and I discussed the idea below a long time (years) ago. Most likely it's also been discussed in this newsgroup a couple of times. Given the state of the compiler back then, back then it seemed like a super cool idea that's entirely realizable, it would just take time for the compiler to become as capable as needed. Nowadays we're in shape to tackle it. Here "it" is. Back when runtime reflection was being discussed, my response was "let's focus on compile-time reflection, and then we can do run-time reflection on demand as a library". Though this might sound sensible, I initially didn't have a design. Now here's what we can do. Currently we have information about symbols as __traits(...) intrinsics wrapped in nice but scattered ways. Now that CTFE is good enough to manipulate structs and arrays thereof, we have the possibility to finally approach things in a nicely unified, structured way. First, we need to prime std.reflection with a few abstractions that characterize entities in a D program. class ModuleInfo { property: string name(); ImportInfo[] imports(); DataInfo[] data(); FunctionInfo[] functions(); ClassInfo[] classes(); StructInfo[] structs(); // includes unions TemplateInfo[] templates(); EnumInfo[] enums(); bool hasStaticCtor(), hasStaticDtor(), hasSharedCtor(), hasSharedDtor(); } Probably there are a few more pieces of data, but you get the idea. Then for each of the entities mentioned above we have a similar definition. For example: enum Protection { isPublic, isPackage, isProtected, isPrivate } class ClassInfo { property: string name(); string baseName(); string parentName(); // if applicable, null otherwise string[] interfaces(); bool isShared(); Protection protection(); DataMemberInfo[] data(); MethodInfo[] methods(); Object defaultConstructor(); ... } Then for an e.g. method declaration we'd have: class MethodInfo { property: string name(); bool isStatic(), isFinal(), isOverride(); Protection protection(); string[] parameterTypes(); string[] parameterNames(); } Some details may vary, e.g. some may be straight members instead of properties etc. (I used properties to allude to use of lazy gathering of information). So so far we have a nice collection of structured data associated with the entities in a D program. Note how this structuring differs yet has similar power to the primitives in std.traits; std.traits offers unstructured bits of information on demand (e.g. ParameterTypeNames) etc. but the objects above group information together per entity declared. All of the above goes in std.reflection, of course. =========== On to primitives that return such data. Given that D can (since relatively recently) create and manipulate class objects during compilation too, it follows that the classes above can be accessed in two ways - through compile-time API and run-time API. When possible, the APIs may even use the same functions; some other times they will be necessarily different. There are two possible approaches to discovering such information. One is by fetching the ModuleInfo for the whole module and navigating it. Another one is by using search primitives from strings. So we should have e.g. // inside std.reflection ModuleInfo getModuleInfo(string moduleName); so a CT call would go like: // client code static info = getModuleInfo("std.algorithm"); whereas a run-time call would be: // client code auto info = getModuleInfo("std.algorithm"); In the latter case, the module needs to save all needed information for ri, so it should plant this: // inside std.algorithm mixin(makeModuleInfoAvailableDynamically()); The mixin would generate all information needed and would store it for later dynamic use. A search API would go like e.g. ClassInfo getClassInfo(string className); In this case the class name could be qualified with module information etc. =========== With this design we unify compile-time and run-time type manipulation in simple ways, by defining structured information about declarations that can be queried during compilation or dynamically. Please chime in with thoughts. Would someone want to pioneer this project? AndreiI don't understand. Are theses structures generated by the compiler or some kind of libraries at compile time on a per needed basis ?
Jul 21 2012
On 7/21/12 8:41 PM, deadalnix wrote:I don't understand. Are theses structures generated by the compiler or some kind of libraries at compile time on a per needed basis ?These are all library structures. Internally they use std.traits, which in turns uses compiler magic with __traits. Andrei P.S. What this thing wit quoting a long message to make a 1-line point? Is that a thing?
Jul 21 2012
On 22/07/2012 06:48, Andrei Alexandrescu wrote:On 7/21/12 8:41 PM, deadalnix wrote:Sounds good, but what about derived classes ?I don't understand. Are theses structures generated by the compiler or some kind of libraries at compile time on a per needed basis ?These are all library structures. Internally they use std.traits, which in turns uses compiler magic with __traits. AndreiP.S. What this thing wit quoting a long message to make a 1-line point? Is that a thing?It is what happen when you editor collapse the text :D
Jul 22 2012
On 2012-07-22 06:48, Andrei Alexandrescu wrote:P.S. What this thing wit quoting a long message to make a 1-line point? Is that a thing?It's the new hip thing :) -- /Jacob Carlborg
Jul 22 2012
Is there a way to get the current module at compile time? __traits(allMembers, pkg.modulename)In the latter case, the module needs to save all needed information for ri, so it should plant this: // inside std.algorithm mixin(makeModuleInfoAvailableDynamically());
Nov 19 2012
On Monday, 19 November 2012 at 11:43:36 UTC, sclytrack wrote: You could do something like this: string currentModule(alias T = {})() { return __traits(parent, T).stringof; }
Nov 19 2012
On 7/21/12, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:class ModuleInfo { property: string name(); ImportInfo[] imports(); DataInfo[] data(); FunctionInfo[] functions(); ClassInfo[] classes(); StructInfo[] structs(); // includes unions TemplateInfo[] templates(); EnumInfo[] enums(); bool hasStaticCtor(), hasStaticDtor(), hasSharedCtor(), hasSharedDtor(); }Are class/struct/function/etc templates going to be stored in the templates field? Then you'd have to tag each template with a type, e.g. "class template" vs "function template" to be able to filter them out. Otherwise classes/functions/etc could have an optional "TemplateTypeInfo[] typeParams" field so you could filter out templated from non-templated types by checking their typeParams field, e.g.: auto tempClasses = filter!(a => !empty(a.typeParams) )(modinfo.classes); I use a similar structure to what you've defined for my code generator and it worked out nicely for me.
Jul 21 2012
On 7/21/12 8:46 PM, Andrej Mitrovic wrote:On 7/21/12, Andrei Alexandrescu<SeeWebsiteForEmail erdani.org> wrote:Perhaps we could accommodate parameterized types together with non-parameterized types by having e.g. additional properties that are null for non-parameterized types.class ModuleInfo { property: string name(); ImportInfo[] imports(); DataInfo[] data(); FunctionInfo[] functions(); ClassInfo[] classes(); StructInfo[] structs(); // includes unions TemplateInfo[] templates(); EnumInfo[] enums(); bool hasStaticCtor(), hasStaticDtor(), hasSharedCtor(), hasSharedDtor(); }Are class/struct/function/etc templates going to be stored in the templates field? Then you'd have to tag each template with a type, e.g. "class template" vs "function template" to be able to filter them out.Otherwise classes/functions/etc could have an optional "TemplateTypeInfo[] typeParams" field so you could filter out templated from non-templated types by checking their typeParams field, e.g.: auto tempClasses = filter!(a => !empty(a.typeParams) )(modinfo.classes); I use a similar structure to what you've defined for my code generator and it worked out nicely for me.That sounds great! Andrei
Jul 21 2012
On Sat, Jul 21, 2012 at 11:44 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: Nice!class ModuleInfo { property: string name(); ImportInfo[] imports(); DataInfo[] data(); FunctionInfo[] functions(); ClassInfo[] classes(); StructInfo[] structs(); // includes unions TemplateInfo[] templates(); EnumInfo[] enums(); bool hasStaticCtor(), hasStaticDtor(), hasSharedCtor(), hasSharedDtor(); }A few questions: 1) About templates, would class/struct/function templates be in there? As in: class Foo(T,U) { ... } => class TemplateInfo { property: string name(); FunctionInfo[] functions(); ClassInfo[] classes(); TemplateInfo[] templates(); // Yes, templates in templates are possible, and so on, recursively ... } 2) Why classes, as opposed to structs? Would inheritance/hierarchies play a role there? Or reference semantics? Note that between structs, classes, templates and modules there is a lot of redundancy. A possibility could be to have an AggregateInfo base class. 3) For a class, a (to my eyes) standard request would be to get the parents chain, up to Object. Could that be added in ClassInfo, or as an external free function? (edit: scratch that, you're talking about lazy gathering below). 4) How would that allows queries like "Here is class C, give me all its available subclasses."? Hmm, wait, I get it: extract classes from the module, and recursively from imported modules. From these classes, extract the parent classes and so on, until the search ranged over the whole inheritance tree. I guess inheritance info could be standard enough for std.reflection to provide such a search. 5) The compiler can emit JSON output giving a partial view of the same information. As a long-term goal, I suggest these infos should be compatible somehow. The JSON output should be enriched, and we should ascertain that using std.json to read this kind of automatically-generated information should give std.reflection infos back. 6) Is really all the necessary info available through std.traits and __traits? Imported modules and their subtilities (renamed functions, etc) seem out of reach, no? 7) I know your examples are not complete, but don't forget aliases and symbols, and module-level values. Since these can be of any type, I'm not sure how they are managed in your scheme. I mean, you cannot have IntInfo[], DoubleInfo[], ... 8) (more a remark than a question) To me, it's another example of a tree-like structure. I humbly suggest we get a std.tree somewhere, to manipulate generic n-ary trees: mapping on trees, searching (extracting elements or whole subtrees), folding trees, etc. That is, a module that deals with trees/graphs not so much as containers, but as a way to represent the relationships between elements. I'm not sure I'm so clear here, but ideally std.json, std.xml, std.reflection and probably a few others should use it as a low-level basis. Btw, I have tree / trees algo (including depth-first / breadth-first ranges) / graph / graph algo (strongly connected components, search in graphs, ...) on github, but it's code that predate std.container and is in limbo, pending std.allocator. Philippe
Jul 22 2012
On 7/22/12 9:39 AM, Philippe Sigaud wrote:1) About templates, would class/struct/function templates be in there? As in: class Foo(T,U) { ... } => class TemplateInfo { property: string name(); FunctionInfo[] functions(); ClassInfo[] classes(); TemplateInfo[] templates(); // Yes, templates in templates are possible, and so on, recursively .... }Yah, ideally all entities definable in a D module should be available via reflection. But I focused on things that e.g. the user of a dynamically-loaded library would be interested in: functions, classes. std.reflection could become the lynchpin for dynamic library use; once the library is loaded (with dlopen or such), the client needs to call getModuleInfo() (a C function that can be found with dlsym()) and then get access to pointers to functions necessary for doing all other work. (Note that my examples don't yet include pointers to executable code yet.)2) Why classes, as opposed to structs? Would inheritance/hierarchies play a role there? Or reference semantics? Note that between structs, classes, templates and modules there is a lot of redundancy. A possibility could be to have an AggregateInfo base class.Initially I used struct, but then I figured reference semantics are more natural for storing cross-entity information, as you indeed took advantage of in your TemplateInfo.3) For a class, a (to my eyes) standard request would be to get the parents chain, up to Object. Could that be added in ClassInfo, or as an external free function? (edit: scratch that, you're talking about lazy gathering below).I used strings because I assumed it's easy to just query the ClassInfo given a string. But yes, we could use references to ClassInfo and InterfaceInfo etc. in a transitive manner.4) How would that allows queries like "Here is class C, give me all its available subclasses."? Hmm, wait, I get it: extract classes from the module, and recursively from imported modules. From these classes, extract the parent classes and so on, until the search ranged over the whole inheritance tree. I guess inheritance info could be standard enough for std.reflection to provide such a search.Something like that. Note that such a query is not particularly OO-ish, because getting a class' cone (totality of subclasses) works against the modularity that inheritance is meant for. I don't think we should make getting class cones particularly easy.5) The compiler can emit JSON output giving a partial view of the same information. As a long-term goal, I suggest these infos should be compatible somehow. The JSON output should be enriched, and we should ascertain that using std.json to read this kind of automatically-generated information should give std.reflection infos back.Yes, that's a great connection that Walter and I discussed a bit.6) Is really all the necessary info available through std.traits and __traits? Imported modules and their subtilities (renamed functions, etc) seem out of reach, no?We'll need indeed to enhance __traits with what's needed. Much of the point of std.reflection is to determine exactly what's there and what's needed. And that starts with the data structures design (the algorithmic aspects are minor).7) I know your examples are not complete, but don't forget aliases and symbols, and module-level values. Since these can be of any type, I'm not sure how they are managed in your scheme. I mean, you cannot have IntInfo[], DoubleInfo[], ...I sort of eschewed part of that by using strings for types.8) (more a remark than a question) To me, it's another example of a tree-like structure. I humbly suggest we get a std.tree somewhere, to manipulate generic n-ary trees: mapping on trees, searching (extracting elements or whole subtrees), folding trees, etc. That is, a module that deals with trees/graphs not so much as containers, but as a way to represent the relationships between elements. I'm not sure I'm so clear here, but ideally std.json, std.xml, std.reflection and probably a few others should use it as a low-level basis. Btw, I have tree / trees algo (including depth-first / breadth-first ranges) / graph / graph algo (strongly connected components, search in graphs, ...) on github, but it's code that predate std.container and is in limbo, pending std.allocator.Well you're the resident crazy-stuff-during-compilation guy. Did you try your trees during compilation? Andrei
Jul 22 2012
On Sunday, 22 July 2012 at 14:28:45 UTC, Andrei Alexandrescu wrote:Yah, ideally all entities definable in a D module should be available via reflection. But I focused on things that e.g. the user of a dynamically-loaded library would be interested in: functions, classes.Plugins are usually done by providing factory methods that create needed objects, the methods are usually obtained with dlsym.
Jul 22 2012
"Kagamin" <spam here.lot> writes:On Sunday, 22 July 2012 at 14:28:45 UTC, Andrei Alexandrescu wrote:That's what you'd do in a language that doesn't have something like D's Object.factory(). In D's case, however, you'd have the "factory method" be the ctor. That's all hypothetical, however, since there's no D ABI for shared objects yet... -- The volume of a pizza of thickness a and radius z can be described by the following formula: pi zz aYah, ideally all entities definable in a D module should be available via reflection. But I focused on things that e.g. the user of a dynamically-loaded library would be interested in: functions, classes.Plugins are usually done by providing factory methods that create needed objects, the methods are usually obtained with dlsym.
Jul 22 2012
On Sun, Jul 22, 2012 at 4:28 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:std.reflection could become the lynchpin for dynamic library use; once the library is loaded (with dlopen or such), the client needs to call getModuleInfo() (a C function that can be found with dlsym()) and then get access to pointers to functions necessary for doing all other work. (Note that my examples don't yet include pointers to executable code yet.)I wouldn't know. I have no experience with dlsym().I realized a few minutes after posting I answered my own question: because this is a self-referencing structure (a tree, a graph), which are easier to code with classes than structs.2) Why classes, as opposed to structs? Would inheritance/hierarchies play a role there? Or reference semantics? Note that between structs, classes, templates and modules there is a lot of redundancy. A possibility could be to have an AggregateInfo base class.Initially I used struct, but then I figured reference semantics are more natural for storing cross-entity information, as you indeed took advantage of in your TemplateInfo.Right. Since people here asked this question, I thought that was a common request in OO. I'm more a structs and mixins guy, myself.4) How would that allows queries like "Here is class C, give me all its available subclasses."? Hmm, wait, I get it: extract classes from the module, and recursively from imported modules. From these classes, extract the parent classes and so on, until the search ranged over the whole inheritance tree. I guess inheritance info could be standard enough for std.reflection to provide such a search.Something like that. Note that such a query is not particularly OO-ish, because getting a class' cone (totality of subclasses) works against the modularity that inheritance is meant for. I don't think we should make getting class cones particularly easy.Good to know.5) The compiler can emit JSON output giving a partial view of the same information. As a long-term goal, I suggest these infos should be compatible somehow. The JSON output should be enriched, and we should ascertain that using std.json to read this kind of automatically-generated information should give std.reflection infos back.Yes, that's a great connection that Walter and I discussed a bit.+1 for enhancing __traits locally. - having __traits(allMembers, xxx) work on simple module names, and not only on qualified package.module names - having a way to get imports and a way to know whether they are static / renaming import As for the data structures, other people's designs allured to in this thread seem similar to your proposal. I have two other questions: About functions: should they be subject to reflection also, or not? They have no fields, inner functions are totally interned, etc. All a user need is the 'interface', right? (name, and everything that's in the type: return type, parameters, purity, etc) About imports, what about inner imports, now that they are authorized in almost any scope? My gut feeling right now is that a user does not care if class C internally import std.algorithm in one of its methods, but I could be wrong.6) Is really all the necessary info available through std.traits and __traits? Imported modules and their subtilities (renamed functions, etc) seem out of reach, no?We'll need indeed to enhance __traits with what's needed. Much of the point of std.reflection is to determine exactly what's there and what's needed. And that starts with the data structures design (the algorithmic aspects are minor).I see. They can be managed like fields in an aggregate (struct / classes), as there are many similarities between D modules and classes / structs. class ModuleInfo { property: ... FieldInfo[] data; // also used in StructInfo and ClassInfo } class FieldInfo { property: string name(); bool isStatic(); Protection protection(); string type(); } As long as this info is available at CT, FieldInfo.type can be mixed-in and used in code. what I'm not sure I get in your design is why some informations are encoded in their own structure, like Protection above (the code is copy-pasted from yours, I'd guess Protection is an enumeration), and then some others are encoded as strings (types). Is that because the values Protection can take are known in advance (and finite)? I wondered whether a design like this could be interesting?: abstract class FieldInfo {} class Field(T) : FieldInfo { property: string name(); bool isStatic(); Protection protection(); alias T Type; } But that doesn't cut it, AFAICT: different fields can be stored in a FieldInfo[] array, but the type information is not easier to get, anyway. So forget it. This kind of manipulation is why I got interested in fully polymorphic trees (tuples of tuples...), able to store any value, while keeping the type information visible. The drastic consequence is to have a tree type depend on its entire content. But I'm coming from Static Typing Land here, whereas this introspection stuff is more dynamic. Anyway, back to gobal values: aliases should be there also. A simple AliasInfo class?7) I know your examples are not complete, but don't forget aliases and symbols, and module-level values. Since these can be of any type, I'm not sure how they are managed in your scheme. I mean, you cannot have IntInfo[], DoubleInfo[], ...I sort of eschewed part of that by using strings for types.Well you're the resident crazy-stuff-during-compilation guy.Ah! I wish. I had this wonderful idea of having code be parsed at CT, semantically analyzed, transformed into some machine code at CT and... , oh wait.Did you try your trees during compilation?Just did. They fail :) Either segmentation fault (core dumped) or an error telling me class literals cannot be returned from CTFE. Hmm, this is old code. I'll have a look since in other projects, I can obtain trees at CT. Philippe
Jul 22 2012
On 23-Jul-12 10:30, Philippe Sigaud wrote:I kind of did it... regex pattern ---parse---> bytecode ---generate D code---> profit. BTW you can avoid machine code, just use D code as glorified backend :) (and get optimizations for free, yay!) -- Dmitry OlshanskyWell you're the resident crazy-stuff-during-compilation guy.Ah! I wish. I had this wonderful idea of having code be parsed at CT, semantically analyzed, transformed into some machine code at CT and... , oh wait.
Jul 22 2012
On Mon, Jul 23, 2012 at 8:41 AM, Dmitry Olshansky <dmitry.olsh gmail.com> wrote:On 23-Jul-12 10:30, Philippe Sigaud wrote:Hey, that was a joke, about implementing a D compiler in D, to be executed at CT by the real D compiler to basically do its job a second time, only worse :) But yes, your regex work is a great example. I should have a look at the code once more.I kind of did it... regex pattern ---parse---> bytecode ---generate D code---> profit. BTW you can avoid machine code, just use D code as glorified backend :) (and get optimizations for free, yay!)Well you're the resident crazy-stuff-during-compilation guy.Ah! I wish. I had this wonderful idea of having code be parsed at CT, semantically analyzed, transformed into some machine code at CT and... , oh wait.
Jul 22 2012
On 23-Jul-12 10:47, Philippe Sigaud wrote:On Mon, Jul 23, 2012 at 8:41 AM, Dmitry Olshansky <dmitry.olsh gmail.com> wrote:Yeah, now I think I got it. Could be a nice benchmark for CTFE though :)On 23-Jul-12 10:30, Philippe Sigaud wrote:Hey, that was a joke, about implementing a D compiler in D, to be executed at CT by the real D compiler to basically do its job a second time, only worse :)I kind of did it... regex pattern ---parse---> bytecode ---generate D code---> profit. BTW you can avoid machine code, just use D code as glorified backend :) (and get optimizations for free, yay!)Well you're the resident crazy-stuff-during-compilation guy.Ah! I wish. I had this wonderful idea of having code be parsed at CT, semantically analyzed, transformed into some machine code at CT and... , oh wait.But yes, your regex work is a great example. I should have a look at the code once more.I'd advice to wait a bit ... it's not really in a good shape (to read it) as there have been tons of improvements in CTFE since the last time I've touched it. I'll have to revisit it and remove some roundabout workarounds. -- Dmitry Olshansky
Jul 22 2012
On 2012-07-22 16:28, Andrei Alexandrescu wrote:Something like that. Note that such a query is not particularly OO-ish, because getting a class' cone (totality of subclasses) works against the modularity that inheritance is meant for. I don't think we should make getting class cones particularly easy.I see no reason to make it difficult on purpose. -- /Jacob Carlborg
Jul 22 2012
On 7/23/12 2:39 AM, Jacob Carlborg wrote:On 2012-07-22 16:28, Andrei Alexandrescu wrote:The reason is that often the cone would be incomplete and therefore confusing. It would also foster non-modular approaches to doing things. AndreiSomething like that. Note that such a query is not particularly OO-ish, because getting a class' cone (totality of subclasses) works against the modularity that inheritance is meant for. I don't think we should make getting class cones particularly easy.I see no reason to make it difficult on purpose.
Jul 22 2012
On Sunday, 22 July 2012 at 13:39:31 UTC, Philippe Sigaud wrote:4) How would that allows queries like "Here is class C, give me all its available subclasses."? Hmm, wait, I get it: extract classes from the module, and recursively from imported modules. From these classes, extract the parent classes and so on, until the search ranged over the whole inheritance tree. I guess inheritance info could be standard enough for std.reflection to provide such a search.You can't do that without breaking the module system – as long as a class is not final (and then it doesn't make much sense to ask for its subclasses anyway), somebody can always extend it in a module completely separate from the code making the query. To illustrate what I mean, let's assume you define a class Foo in a module A. The user imports A from a second module B, and potentially extends Foo there. Now, if it was possible to get all possible subclasses in A, this would lead to information leaking from B to A, while the import graph only allows the other direction. As a consequence, all kinds of issues related to order dependence, separate compilation, etc. would arise. David
Jul 23 2012
On Mon, Jul 23, 2012 at 2:43 PM, David Nadlinger <see klickverbot.at> wrote= : [getting subclasses]You can't do that without breaking the module system =E2=80=93 as long as=a class isnot final (and then it doesn't make much sense to ask for its subclasses anyway), somebody can always extend it in a module completely separate fr=omthe code making the query. To illustrate what I mean, let's assume you define a class Foo in a modul=eA. The user imports A from a second module B, and potentially extends Foo there. Now, if it was possible to get all possible subclasses in A, this would lead to information leaking from B to A, while the import graph onl=yallows the other direction. As a consequence, all kinds of issues related=toorder dependence, separate compilation, etc. would arise.Yes, I know. That would be a purely local question: from module A, what subclasses of class Foo do I see? Anyway, maybe I'm warped by CLOS.
Jul 23 2012
On Saturday, 21 July 2012 at 21:44:52 UTC, Andrei Alexandrescu wrote:Please chime in with thoughts. Would someone want to pioneer this project?There are some questions. 1. static info = getModuleInfo("std.algorithm"); The language does not allow you to use CTFE parameter values as arguments to __traits/templates. Therefore, to be able to build meta-objects at compile-time, you would have to: static info = getModuleInfo!"std.algorithm"; 2. Then, what is the preferred way of referencing compiler objects - with strings or directly: import std.algorithm; static info = getModuleInfo!(std.algorithm); ? I think using strings while we have direct access to those objects is not an incredibly good idea. Those strings will be mixed-in by getXxxInfo anyway: ModuleInfo getModuleInfo(string name)() { ... mixin("import " ~ name ~ ";"); mixin("alias __traits(getMembers, " ~ name ~ ") members;"); foreach (m; members) ... } 3. auto info = getModuleInfo("std.algorithm"); There is no way to mutate global data structures at compile-time, therefore you would have to build, at run time, a data structure aggregating all meta-objects coming from various modules. That could be achieved with static constructors: module std.reflection; // application-wide module info registry private ModuleInfo[string] moduleInfos; // run-time module info getter string getModuleInfo(string s) { return moduleInfos[s]; } string makeModuleInfoAvailableDynamically() { return q{ shared static this() { shared static mi = getModuleInfo!(__traits(thisModule)); moduleInfos[mi.name] = mi; } }; } Problematic because mixing-in makeModuleInfoAvailableDynamically would mean that circular imports are no longer allowed for that module. Remember the whining babies complaining about this very use case a while ago? What about changing the language so that static constructors marked with system are exempted from circular dependency checking? 3. If you are against inheritance, why classes and not structs? 4. How the whole thing is intended to interact with dynamic link libraries?
Jul 22 2012
On Sun, Jul 22, 2012 at 5:10 PM, Max Samukha <maxsamukha gmail.com> wrote:The language does not allow you to use CTFE parameter values as arguments to __traits/templates. Therefore, to be able to build meta-objects at compile-time, you would have to: static info = getModuleInfo!"std.algorithm";Maybe I don't get your comment, but AFAICT, the language does allow you to use CTFE parameters values as arguments to templates: template Twice(double d) { enum Twice = d * 2; } double foo(double d) { return d+1.0; } void main() { enum t = Twice!(foo(1.0)); pragma(msg, t); }
Jul 22 2012
On Mon, 23 Jul 2012 08:32:37 +0200, Philippe Sigaud <philippe.sigaud gmail.com> wrote:Maybe I don't get your comment, but AFAICT, the language does allow you to use CTFE parameters values as arguments to templates: template Twice(double d) { enum Twice = d * 2; } double foo(double d) { return d+1.0; } void main() { enum t = Twice!(foo(1.0)); pragma(msg, t); }Wrong way around. Try this: template Twice(double d) { enum Twice = d * 2; } double foo(double d) { return Twice!d; } void main() { enum t = foo(1.0); pragma(msg, t); } -- Simen
Jul 23 2012
On Mon, Jul 23, 2012 at 9:46 AM, Simen Kjaeraas <simen.kjaras gmail.com> wrote:Wrong way around. Try this: template Twice(double d) { enum Twice = d * 2; } double foo(double d) { return Twice!d; } void main() { enum t = foo(1.0); pragma(msg, t); }Ah, that, OK. And __ctfe does not help. Would it really limit the proposed scheme for std.reflection?
Jul 23 2012
On 7/22/12, Philippe Sigaud <philippe.sigaud gmail.com> wrote:2) Why classes, as opposed to structs?I think either way you'd need reference semantics. For example maybe you're doing code-generation at compile-time but you need to rename a class name in a typeinfo returned by std.reflection before doing any processign. With reference semantics you only have to change one class and all other types which refer to such a class will have access to the new name. You could use structs as well, and actually I use structs in my codegenerator (with a similar layout to what Andrei posted). Each struct (e.g. Class/Function) stores Symbols, which are structs with an ID and a Type. I can look up each symbol in a SymTable which actually holds the structures with data. So a Symbol is like a fake pointer, and the SymTable would be the memory. I originally planned to use classes but some serialization frameworks didn't work with those so I settled using structs and a bit of template mixin magic instead. All of this std.reflection talk is quite exciting actually. If you had AST information about your entire D library you could do some really cool things. You could make a better documentation generator than ddoc, or export the AST into a file for a code-completion plugin, or create a wrapper C library which enables other languages to use your D library.
Jul 22 2012
On 2012-07-21 23:44, Andrei Alexandrescu wrote:Walter and I discussed the idea below a long time (years) ago. Most likely it's also been discussed in this newsgroup a couple of times. Given the state of the compiler back then, back then it seemed like a super cool idea that's entirely realizable, it would just take time for the compiler to become as capable as needed. Nowadays we're in shape to tackle it. Here "it" is. Back when runtime reflection was being discussed, my response was "let's focus on compile-time reflection, and then we can do run-time reflection on demand as a library". Though this might sound sensible, I initially didn't have a design. Now here's what we can do.I've been waiting for this :) -- /Jacob Carlborg
Jul 22 2012
On Sun, Jul 22, 2012 at 6:15 PM, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:On 7/22/12, Philippe Sigaud <philippe.sigaud gmail.com> wrote:Yeah, I agree, reference semantics look better. And since there is a lot of duplication (struct info is a subtype of class info, itself looking very uch like a module info, I guess a mini-hierarchy is possible). So I'm convinced classes look like a good fit.2) Why classes, as opposed to structs?I think either way you'd need reference semantics.You could use structs as well, and actually I use structs in my codegenerator (with a similar layout to what Andrei posted). Each struct (e.g. Class/Function) stores Symbols, which are structs with an ID and a Type. I can look up each symbol in a SymTable which actually holds the structures with data. So a Symbol is like a fake pointer, and the SymTable would be the memory. I originally planned to use classes but some serialization frameworks didn't work with those so I settled using structs and a bit of template mixin magic instead.Could you describe your design in this thread, and possibly link to some code?All of this std.reflection talk is quite exciting actually.It is!If you had AST information about your entire D library you could do some really cool things. You could make a better documentation generator than ddoc, or export the AST into a file for a code-completion plugin, or create a wrapper C library which enables other languages to use your D library.You could do macros. 'nuff said.
Jul 22 2012
On 2012-07-23 08:35, Philippe Sigaud wrote:On Sun, Jul 22, 2012 at 6:15 PM, Andrej MitrovicIf I recall correctly, Scala uses its reflection API as a part off its macros. -- /Jacob CarlborgIf you had AST information about your entire D library you could do some really cool things. You could make a better documentation generator than ddoc, or export the AST into a file for a code-completion plugin, or create a wrapper C library which enables other languages to use your D library.You could do macros. 'nuff said.
Jul 23 2012
Huge interest! I wouldn't want to see static data structures bloat/clutter the exe though in cases where it's not ever used/queried. The object factory is already a serious problem. On 22 July 2012 00:44, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>wrote:Walter and I discussed the idea below a long time (years) ago. Most likely it's also been discussed in this newsgroup a couple of times. Given the state of the compiler back then, back then it seemed like a super cool idea that's entirely realizable, it would just take time for the compiler to become as capable as needed. Nowadays we're in shape to tackle it. Here "it" is. Back when runtime reflection was being discussed, my response was "let's focus on compile-time reflection, and then we can do run-time reflection on demand as a library". Though this might sound sensible, I initially didn't have a design. Now here's what we can do. Currently we have information about symbols as __traits(...) intrinsics wrapped in nice but scattered ways. Now that CTFE is good enough to manipulate structs and arrays thereof, we have the possibility to finally approach things in a nicely unified, structured way. First, we need to prime std.reflection with a few abstractions that characterize entities in a D program. class ModuleInfo { property: string name(); ImportInfo[] imports(); DataInfo[] data(); FunctionInfo[] functions(); ClassInfo[] classes(); StructInfo[] structs(); // includes unions TemplateInfo[] templates(); EnumInfo[] enums(); bool hasStaticCtor(), hasStaticDtor(), hasSharedCtor(), hasSharedDtor(); } Probably there are a few more pieces of data, but you get the idea. Then for each of the entities mentioned above we have a similar definition. For example: enum Protection { isPublic, isPackage, isProtected, isPrivate } class ClassInfo { property: string name(); string baseName(); string parentName(); // if applicable, null otherwise string[] interfaces(); bool isShared(); Protection protection(); DataMemberInfo[] data(); MethodInfo[] methods(); Object defaultConstructor(); ... } Then for an e.g. method declaration we'd have: class MethodInfo { property: string name(); bool isStatic(), isFinal(), isOverride(); Protection protection(); string[] parameterTypes(); string[] parameterNames(); } Some details may vary, e.g. some may be straight members instead of properties etc. (I used properties to allude to use of lazy gathering of information). So so far we have a nice collection of structured data associated with the entities in a D program. Note how this structuring differs yet has similar power to the primitives in std.traits; std.traits offers unstructured bits of information on demand (e.g. ParameterTypeNames) etc. but the objects above group information together per entity declared. All of the above goes in std.reflection, of course. =========== On to primitives that return such data. Given that D can (since relatively recently) create and manipulate class objects during compilation too, it follows that the classes above can be accessed in two ways - through compile-time API and run-time API. When possible, the APIs may even use the same functions; some other times they will be necessarily different. There are two possible approaches to discovering such information. One is by fetching the ModuleInfo for the whole module and navigating it. Another one is by using search primitives from strings. So we should have e.g. // inside std.reflection ModuleInfo getModuleInfo(string moduleName); so a CT call would go like: // client code static info = getModuleInfo("std.algorithm")**; whereas a run-time call would be: // client code auto info = getModuleInfo("std.algorithm")**; In the latter case, the module needs to save all needed information for ri, so it should plant this: // inside std.algorithm mixin(**makeModuleInfoAvailableDynamic**ally()); The mixin would generate all information needed and would store it for later dynamic use. A search API would go like e.g. ClassInfo getClassInfo(string className); In this case the class name could be qualified with module information etc. =========== With this design we unify compile-time and run-time type manipulation in simple ways, by defining structured information about declarations that can be queried during compilation or dynamically. Please chime in with thoughts. Would someone want to pioneer this project? Andrei
Nov 19 2012
I like it. First a couple details:class ClassInfo { property: string name(); string baseName(); string parentName(); // if applicable, null otherwise string[] interfaces(); bool isShared(); Protection protection(); DataMemberInfo[] data(); MethodInfo[] methods(); Object defaultConstructor(); ... }Why do you not use recursion here? Why not ClassInfo base(); ClassInfo parent(); and then you get baseName by calling base.name;Then for an e.g. method declaration we'd have: class MethodInfo { property: string name(); bool isStatic(), isFinal(), isOverride(); Protection protection(); string[] parameterTypes(); string[] parameterNames(); }Do you think there'll be any way to get the actual types of the arguments and the return type back from the struct? I think that without compiler magic or an uncomfortable amount of templates it probably won't be possible, but I might be wrong. Now to my big complaint: I really dislike making the runtime api accessible through a mixin. I'd much prefer making it a non-member template. Reasons are a) I don't want to store the runtime information in my type. A vtable pointer should be enough to get that information. Maybe use it as an index into a global immutable associative array. For modules and non-class types the name can act as a unique identifier. b) It'd make it possible to get runtime information on types that I get from libraries. c) It would lead to less coupling. d) It would discourage writing types that rely on runtime reflection, but it would not make it impossible.
Nov 19 2012