dao
dao copied to clipboard
Enhancement: DE (Dao Enhancements) project and process
This is a very long-term enhancement and really not an enhancement to the language itself, but to the process of developing it.
I reference python a lot. This is another case where Python as an open source project has implemented a solution for proposing and discussing enhancements that works really well. I feel like the github issue page isn't the best place for discussing core changes and additions to the language; it seems better suited for bugs.
So I propose that Dao implement something similar to the Python Enhancement Proposals (PEP) site: https://www.python.org/dev/peps/
In keeping with the Taoism theme, I propose that for Dao this be called "DE" (Dao Enhancements).
The enhancement index could then be referred to as the Dao DE Jing (Book of Dao DEs). (Yes, it's redundant. But still.)
I feel discussion about proposed enhancements will be both better structured and more effective if PEP's general methods are followed: filing enhancements as a document containing:
- Abstract (summary of the proposal)
- Specification (how the proposed feature will work, including syntax and semantics changes)
- Motivation ("why the existing language specification is inadequate to address the problem that the PEP solves")
- Rationale (The reasoning for the specific design proposed, as well as why any other considered designs were rejected, and references to how other languages approach the feature)
- Backward compatibility (Any parts of the implementation that will break existing code, and how these breakages should be dealt with)
The discussion should then occur on a mailing list or some other forum, and only the final decision should be posted to the DE itself once it has been made. The DE could be updated over time as a result of discussions on the forum, but the discussions should be separated from the official filing.
I believe this will provide a stronger framework both for the proposal of features (as following this format will make these proposals much more complete and easier to understand both the implementation details and consequences thereof) and for the discussion and, later, implementation of the features. (At implementation time, a DE effectively becomes a design document that gives the developer implementing it a clear reference to design the implementation as well as unit tests around).
That is a fairly reasonable approach, albeit it doesn't match the current scale of development and usage of Dao. It makes sense in the perspective, but for now we have a lot of pressing issues to solve.
Nevertheless, it's good that you brought this up. Changes to the language has often been done in spontaneous manner; a better organization of enhancements may eventually be desirable.
That is a fairly reasonable approach, albeit it doesn't match the current scale of development and usage of Dao.
Agreed, that's why I said it's a long term goal. But I thought of the name "De" for it and it seemed too perfect to keep it to myself. ;)
Changes to the language has often been done in spontaneous manner
The number of things that are completely different from the documentation that I've had to figure out from examining the dao source... I had a feeling that was probably the case. Figuring out that "operator " had become "routine " was fun. (I also imagine that change will make typecast operators more difficult to implement (I saw a todo in the documentation indicating something about those) since there's not a special operator keyword anymore.)
I also imagine that change will make typecast operators more difficult to implement
Nope, no problem. It's routine (targetType)(){ ... }.
Aha! Neat! I kept trying to figure out how to do it and couldn't. Thanks for clearing that up! And that works implicitly with io.write(); that's EXACTLY what I wanted.
This is totally the wrong place to ask this (wish there were a good place for me just to ask questions other than in bug discussions) but is there also a way to make a class iterable, so I can use the for(value in myClassInstance){} syntax on it?
I'll also throw in a couple of additional questions:
- Any way to explicitly unbind/delete a variable? i.e., in python I can use
del varName. Mostly I don't care about this, but if something like this exists it would make testing things out in the interactive console easier. - Any way to enable calling with keyword arguments? e.g.:
routine PrintSomeNumbers(i = 0, j = 0, k = 0)
{
io.writeln(i, j, k);
}
PrintSomeNumbers(k=1);
- Is there any support for weak references?
- Is there any ability to get or set a class field by string, as with python's
getattr/setattr/hasattr?
This is totally the wrong place to ask this (wish there were a good place for me just to ask questions other than in bug discussions) but is there also a way to make a class iterable, so I can use the for(value in myClassInstance){} syntax on it?
There is an example in demo/interface.dao. Basically, you need to define for operator to instantiate the loop and [] operator to yield the next value and advance the iterator.
There is an example in demo/interface.dao. Basically, you need to define for operator to instantiate the loop and [] operator to yield the next value and advance the iterator.
Thank you. :)
That brings up another question, though. The documentation says you can inherit from an interface as if it were a class. But when I try this:
interface AA
{
routine Meth( a = 0 );
routine[]( index : int )=>int;
routine.name()=>string;
routine for( iter : ForIterator );
routine[]( iter : ForIterator )=>int;
}
class BB : AA
{
routine Meth( a = 0 ){ io.writeln( a ) }
routine[]( index : int ){ return index }
routine.name(){ return "BB" }
routine for( iter : ForIterator ){
iter.valid = 1;
iter.iterator = 0;
}
routine[]( iter : ForIterator ){
id = (int)iter.iterator;
iter.valid = id + 1 < 5;
iter.iterator = id + 1;
return id;
}
}
It gives me this:
[[ERROR]] in file "code string":
At line 14 : Invalid class definition --- " class BB : AA { routine Meth( a ... ";
At line 14 : Need symbol of class or C type --- " AA ";
Has the syntax for inheriting from interfaces changed?
Nevermind. I misread the documentation. An interface can inherit from another interface, but a class can't inherit from an interface, is that correct? Is there any way to force a compile-time check that all interface functions have been implemented even if the class is never instantiated, let alone passed to a function?
This issue has suddenly become a Q&A thread. Once my questions are answered I propose possibly deleting this bit of conversation as it's very much unrelated to the proposal.
Any way to explicitly unbind/delete a variable? i.e., in python I can use del varName. Mostly I don't care about this, but if something like this exists it would make testing things out in the interactive console easier.
Not that bluntly. A nice way to achieve this is by using a @T|none variable, which can be set to none when you no longer need the actual value.
Any way to enable calling with keyword arguments? e.g.: routine PrintSomeNumbers(i = 0, j = 0, k = 0) { io.writeln(i, j, k); } PrintSomeNumbers(k=1);
A bit more complicated then you could think.
routine PrintSomeNumbers(...: tuple<enum<i,j,k>,int> as args) { # named params captured into 'args' tuple
for (x in args) { # for each (name, value) where name is empty and value is tuple<enum,int>
invar name = ((string)x[1][0])[1:] # param name casted from symbol to string without the leading '$'
invar value = x[1][1] # the value
io.writeln(name, '=', value)
}
}
If you invoke PrintSomeNumbers(k = 1), args will look as (($k, 1),). enum field in the tuple may be specified without acceptable symbols (e.g., tuple<enum,int>), in which case you can use any parameter names. But the parameters should be variadic, as you can't change the order in which ordinary arguments are passed to a routine.
If you wonder why so odd comparing to Python and others, it's mainly because of problems with overloading. However, it's a very flexible design; you may look into my web.html module to see how named parameters can be applied.
Is there any support for weak references?
No.
Is there any ability to get or set a class field by string, as with python's getattr/setattr/hasattr?
Not by ordinary means. There is protobject module for prototype-based OOP where fields are created on demand, but it's a different thing.
Nevermind. I misread the documentation. An interface can inherit from another interface, but a class can't inherit from an interface, is that correct? Is there any way to force a compile-time check that all interface functions have been implemented even if the class is never instantiated, let alone passed to a function?
Probably not. But nothing prevents you from declaring a dummy function for that which you'll never actually call.
Not that bluntly. A nice way to achieve this is by using a @T|none variable, which can be set to none when you no longer need the actual value.
Doesn't really help me since my main goal is being able to reuse variables for different types in the interactive console. Really doesn't matter though. I just have to remember to declare them as any and I get the same thing.
If you wonder why so odd comparing to Python and others, it's mainly because of problems with overloading. However, it's a very flexible design; you may look into my web.html module to see how named parameters can be applied.
Makes sense, I'm not overly stuck on this feature anyway, was just a curiosity. The fact that it exists at all is still nice - it's not a feature I use all that much even in python, but there are some cases where it makes for a nicer interface. It is a little ugly to look at, but it works.
Is there any support for weak references?
No.
That's probably something I'll open a feature request for at some point, then. Eventually. Another case of "I use these a lot," but I'll try to put in the effort to write up a proper, detailed proposal this time.
Is there any ability to get or set a class field by string, as with python's getattr/setattr/hasattr?
Not by ordinary means. There is protobject module for prototype-based OOP where fields are created on demand, but it's a different thing.
Yeah, it's a different thing. I can always fall back on std.eval() if I ever need this, but I tend to find a more direct interface like that to be cleaner. Seems like this might show up in meta some day, though.
Not that I really need this feature a lot anyway, so again, mostly comes down to curiosity. A lot of the cases I can think of for wanting this are solved much more nicely by interfaces and function overloading.
Nevermind. I misread the documentation. An interface can inherit from another interface, but a class can't inherit from an interface, is that correct? Is there any way to force a compile-time check that all interface functions have been implemented even if the class is never instantiated, let alone passed to a function?
Probably not. But nothing prevents you from declaring a dummy function for that which you'll never actually call.
That feels a little hacky. But I don't find the way it's implemented now that offensive. I can't really think of any practical reason to create a class and then not use it. It's just different from what I'm used to - but that's not necessarily bad.
Most of these questions were just "I'm curious." Weak references are probably the only one I care enough about to create a feature request for them.
Is there any ability to get or set a class field by string, as with python's getattr/setattr/hasattr?
Not by ordinary means. There is protobject module for prototype-based OOP where fields are created on demand, but it's a different thing.
Actually, it seems meta does have this! It has meta.variable() and meta.constant() that can get variables, and meta.variables() and meta.constants() that can list them, though they seem to be broken in that the name is always empty.
class MyClass
{
routine MyFunc() {}
var myVar;
}
var mc = MyClass()
io.writeln(meta.constant(mc, "MyFunc"))
io.writeln(meta.variable(mc, "myVar"))
io.writeln(meta.variable(mc, "myVar", 3))
io.writeln(mc.myVar);
Output:
routine_17_0x68fc90
( none, type<var<@X>>_21_0x690278 )
( none, type<var<@X>>_21_0x690278 )
3
Weak references are probably the only one I care enough about to create a feature request for them.
There is no need for such thing in Dao, because Dao GC is capable of collecting cyclically referenced objects.
@daokoder
Weak references have other uses besides avoiding cyclic references. I use them frequently for caching systems, where an object only has a strong reference in the cache itself and in objects actively working with them, but other things keep a weak reference to allow them to be ejected from the cache. The same could be accomplished by keeping a cache key instead of a reference, but weak references provide a performance optimization by not having to perform the hash lookup over and over.
I'll write up a formal proposal at some point detailing the uses that weak references have even in non-garbage collected systems. (I use them in C++, for example.)
I'll write up a formal proposal at some point detailing the uses that weak references have even in non-garbage collected systems. (I use them in C++, for example.)
No need to be formal, just make a thorough explanation why the feature is important and why it can't be substituted by anything else.
@ShadauxCat did you find some time to write summarize your findings and ideas regarding weak references? It would interest me as I was always confident we shall not write performance oriented caches (or alike) directly in Dao, but rather as a C module. But maybe there is a way how to add (partial) support for weak references to Dao in a non-intrusive way (even if it would be e.g. a C module providing a "universal" cache interface :wink:).