[Gambas-user] Re: a few suggestions for improvement

Toni Schornboeck lists at ...706...
Fri Jan 7 10:24:31 CET 2005


Benoit Minisini writes:
>> After a Form is closed, the object still lives, but at the same time is
>> dead.
>> Well, I dont find that ugly, and if you have several instances of your form, 

> just don't declare the variable static.

It was my bad. Thanks to Oliver Stieber for providing this workaround 
without
a static variable. 

But there is another problem with dieing forms:
For example in .NET a Dialog has also a Property which indicates which 
button
was pressed to close the form. 

The thing I want to achieve is 'easier' coding: 

dim f as Form
f=new Form(bar)
f.ShowModal()
if f.Closed = Form.Cancel then return
handle_value(f.Value) 

would be very nice :) The 'Problem' with Oliver's workaround is: If I return
an object, I can return NULL to say "user pressed cancel" but if I want to 
return
a primitive type like int or so, that is not possible. And 3 states (for 
example:
Ignore, Retry, Cancel) wouldn't work. 

Of course, one can workaround this - but more than one return value by 
function
would add complexity that wouldn't be necessary. 

And I personally don't like it, when an object dies without me calling 
Close(), a
destructor, finalize() or any other explicit killing method. 

I think the code is not intuitive when an object dies because I called 
Show() on it... 

But the workaround from Oliver is a good solution. 

>> A constructor has always to be public. Why?
>It is by design.

Could you consider changing this or adding an other method for prohibiting 
client code
to instanciate an object?
When writing a complex class hierachies i find it sometimes necessary to 
force the user to
use a factory instead of instanciating object by himself. 


>> I'd like to declare a constructor private to force the user of my code to
>> use a static
>> construct (that means: a static function that returns the object).
>> This can sometimes be useful, for example when using a factory to prohibit
>> the user from directly instanciating
>> objects.
>At the moment, I don't see any mean to do that cleanly.

For example:
A XML Parser which holds concrete Nodes:
All are subclasses of NodeBase 

The Parser doesn't care about the different Nodes, it just calls
parentNode.AddChild(CreateNodeFromTag(tag)) 

it wouldn't make any sense to create a specific Node. So CreateNodeFromTag() 
creates a
Node from the Tag and takes into account that some Tags produce the same 
Node class and so on. 

The idea behind this is: client code need not to have any clue about the 
different node classes,
they just don't show. All is hidden behind NodeBase. For the client code it 
doesn't matter if
the tag "<foo>" creates a FooNode or BarNode, it just calls node.Process() 
and the node does its job :) 

Of course it works without defining private constructors, but it could help 
to keep the user from
doing odd things he is not supposed to do. 

>> This leads to another point: friends could be useful or something like Java
>> Packages where one can declare a method
>> as package private.
>In Gambas, public means exported. If something is private in a class, then the 
>interpreter can't access it from outside the class. So, constructors, 
>destructors, event handlers... need to be public, or the interpreter won't be 
>able to call them.

I understand. 

But for further versions, would it be possible to consider a package system 
like Java's?
The idea is:
exporting not all classes and methods.
That could be usefull when writing libraries in gambas. 

The Java Collections show us, how nice this can be when not everything is 
visible for the client. 

And of course there are often internal helper classes. These should be also 
kept private and hidden :)
So we can keep the public interface of a library very slim and clean. 

>> optional parameters are very useful, but why can I only assign a default
>> value to primitive types?
>I think it is just something to change in the compiler. So it may be 
>possible :-)
That would be very helpful and would add clarity to code.
Because I think one of the important things about a language is consistency.
There shouldn't be unecessary differences between primitive types and 
objects. 


>> The workaround using
>> if isnull(o) then o=new Bar(baz)
>> doesn't always work.
>> Because NULL may be a perfectly legal value in some cases.
>Well, in your example, NEW will never return NULL.
Yes, this would be just a workaround: 

public sub foo(o as Bar)
 if isnull(o) then o=new Bar(baz)

instead of
public sub foo(o as Bar = new Bar(baz)) 

but the difference is:
foo(null)
will yield different results, because my first version would assume that no 
value was provided
and override null with new Bar(baz) but maybe null would be a correct value 
and not the default one ->
this would be impossible to implement. 

>> for each on collections acts weird.
>I don't unserstand what you say. Collection.Key is just a read-only property 
>that returns the key of the last entry accessed in the collection. I don't 
>see where the collection is modified during the iteration...

I'm sorry, I haven't explained enough because I thought my point would be 
obvious (which was stupid of me
because if it is obvious and clear to you, you wouldn't have implemented in 
that way ;)) 

So here is a better explaination: 

The problem as I see it are some very hard to find bugs: 

for each v in col
 if ShallEntriesBeDeleted(col.Key) then
   DeleteAllEntriesLike(col, col.Key)
 end if
 LogMessage("All Entries Like " & col.Key & " have been deleted")
next 

See the bug?
col.Key in
DeleteAllEntriesLike(col, col.Key)
and in
LogMessage("All Entries Like " & col.Key & " have been deleted")
are 2 different values.
And to add obscurity: the value of col.Key in
LogMessage("All Entries Like " & col.Key & " have been deleted")
deepends on the implementation of DeleteAllEntriesLike 

The problem is:
col.Key can change everytime I pass col to an extern function. I can't know
if it will change or not, but it can change. 

So I have to store col.Key in a separate variable.
Now the reader of my code has to figure out, if I'm using col.Key or a 
separate variable.
This add complexity. 

>> I really miss a 'super' or 'base' keyword to access the Baseclass from
>> within the derived class.
> You are right, SUPER is missing. It's in my TODO list.
Thank you. 

>> Why can't I define which parameter are passed to my base class?
> Try that: 
> 
> BaseNode.class: 
>
> PUBLIC SUB _new()
> ...
> END 
>
> SomeNode.class: 
>
> PUBLIC SUB _new(allowedChildren)
> ...
> END 
> 
> Each class in the inheritance hierarchy consumes the parameters passed to the 
> constructor. 

Yes, and so I have to implement a SetAllowedChildren() function, or do I 
miss an important
point? 

BaseNode holds all allowedChildren but it doesn't know what they are. 
SomeNode has to set
all allowedChildren when it is created. currently SomeNode can't pass any 
parameter to BaseNode's
constructor. SomeNode has to use a SetAllowedChildren() function, which 
allowes to change the nodes
during runtime, which shouldn't be allowed.
OK, one can introduce a sealed flag, which can be set once and afterwards 
never changed to indicate if
SetAllowedChildren() will succeed. 


>> I already mentioned the lack of casting Operators.
> If I remember, you can do ComboBox(...) to cast to ComboBox. Test it to see if 
> it really works.
Oh, I'm sorry. It works perfectly.
I must have overlooked it in the documentation, sorry. 

>> This leads to: variables must be declared at begin of function
>> why is that?
> I find declaring variable everywhere a source of confusion and unreadable 
> code. So I force them to be declared at the beginning of the function. And 
> one by line at the moment ?

One by line is good practice, but only at begin of scope?
But I see, we can argue as much as we like, but neither of us will change 
his mind about
that, right? 

FOR?? 

>> It would be really important to allow the user to declare variables as
>> local as possible.
> You are not compelled to write large functions...

I know, and 90% of the functions in my current work are less than 10 lines 
of code.
But there are a few functions that just initialize something.
They take more than 10 lines, but I think it is OK, because it is very easy 
to understand
the meaning (they just initialize the components on the form) 

And sometimes there are a few variables that are only needed at the end of 
the function...
OK, I'll stop arguing ;) 

>> A very controversal point is:
>> no RETURN will return NULL
> It is by design.
I understand. 


>> One thing that really annoys me is:
>> arr.Add(new Foo(bar))
>> doesn't work.
> It is by design too. I didn't want to make NEW a true operator. But it is just 
> a compiler restriction. The interpreter don't care.
> For example, you can do 
>
> arr.Add(New("Foo", bar))
Oh.
Why doesn't the compiler then allow it?
If it is by design, why does the interpreter allow it.
And what are the benefits from not allowing it? 

I'm sorry, but I really can't see any good reason for this :( 

>> another nice feature would be, if one could declare a variable inside a for
>> statement: 
>>
>> for i as integer = 0 to 10
> Same remark than before. And I don't see the benefit of having variables "as 
> local as possible". It will change nothing in interpreted code.

We have really different religions :)
One of my most important laws is "declare variables as local as possible" 

>> Why doesn't gambas do virtual dispatching?
> You are absolutely right. I just change the interpreter in the 2.0 so that 
> virtual dispatching work. As soon as I release the first developmement 
> version (1.9.1), you will be able to test it and tell me if things are right.
Thank you! 

>> I'd love to see the possibility to set the Tabstop ordering.
> TabOrder = ZOrder, so you just have to modify the second to change the first.
I'm sorry. I must have missed it in the documentation.
Thank you! 

>> It would improve the usability of the IDE if I could create directories to
>> store my class and form files
> I was thinking of letting the IDE show an ordered and hierarchical view of 
> forms, classes and modules, even if they are stored flat in the project 
> directory.

Something like an "object browser"?
Would be nice, but I'd like to set it also myself. 

Consider a large project that uses 5 different libraries written in Gambas:
a library consist of classes, but these classes are not required to inherit 
from
each other - so your object browser wouldn't display the libraries correctly 
(because
it would split them). 

But I agree, an object browser would be great. 

>> a minor issue: the executale file of my project contains a lot of
>> information that shouldn't be there
> Well. It is a point I didn't think about :-) I note it in the TODO list.
Thank you. 

 

I'm glad that you took the time to answer my questions/suggestions.
Even though we don't agree with each other everywhere, I hope you'll 
consider a few
of my points for future version of gambas :) 


PS: sorry for answering this late, but I haven't work yesterday -> we had a 
public holiday




More information about the User mailing list