Re: [fpc-pascal] "is"

2005-04-07 Thread Thomas Schatzl
Hello,
Michael Van Canneyt schrieb:
For example;
MI for interfaces for me is strange (to say the least) because
interfaces were introduced to avoid the mess of MI in the first
place. So why on earth would you want to introduce it ??
Because MI itself isn't bad, it's often an appropriate representation of
some abstract model. There's only the problem when there are different
implementations for the same method, that's why it is generally avoided. 
This is multiple _implementation_ inheritance. Commonly considered bad =)

Since interfaces do not have/inherit implementations by definition this
is not an issue, they simply aggregate function *specifications* (no 
code). This is multiple _interface_ inheritance. Commonly considered good =)

Effectively it cuts down typing involved in creating interface
hierarchies dramatically, or actually enabling some things. Another 
thing that comes to my mind is that it allows some clever tagging with 
empty interfaces (without saying anything of importance).

Example (probably not ideal, but I hope mostly self-explaining):
type
  IInput = interface
... method specifications ...
  end;
 IOutput = interface
... method specifications ...
 end;
 IInOut = interface(Input, Output)
... method specifications ...
 end;
 IBlockdeviceReaderWriter = interface(IInout, ...)
... method specifications ...
 end;
 IStreamedReaderWriter = interface(IInout, ...)
   ... method specifications ...
 end;
 TSomeClass = class(, IBlockdeviceReaderWriter)
   ... method specs + implementations ...
 end;
 TSomeOtherClass = class(, IStreamReaderWriter)
 end;
 [...]
- You can use both TSomeClass and TSomeOtherClass in methods using an 
IInOut now, this includes easy testing that TSomeClass and 
TSomeOtherClass have the required capabilities by

var instance : either TSomeClass or TSomeOtherClass;
SomeProcessingMethod(x : IInout);
  and
SomeProcessingMethod(x as IInout);
Not
SomeProcessingMethod(x : TObject);
  and
SomeProcessingMethod(x);
and test that x implements both IOut and IIn before using it in the 
method (or before calling the method, or using overloading in some cases).

Still both implementations retain the flexibility of being allowed to be 
derived some different ancestors _if needed and appropriate_.

- if you add a subinterface to IInOut you'd have to revise all your code 
and add this interface to all class specifications... I don't hope you 
forget one (since the "as" is evaluated at compile time, happy bug hunting).

- tagging of instances with (empty) interfaces and use of existing 
methods for checking.

type
IMammal = interface;
IMeatEater = interface;
IPlantEater = interface;
IEatsEverything = interface(IMeatEater, IPlantEater);
IRat = interface(IMammal, IEatsEverything);
Checking an instance for being a rat (implementing IRat) is just a 
matter of "supports(instance, IRat)", not necessarily 
"supports(instance, IMammal) and supports(instance, IMeatEater) and 
supports(instance, IPlantEater)".

In Pascal sets may be used for that. But consider that every one of 
those interfaces specifies one or more particular methods... (maybe this 
example is better than the above one after all). There's no way other 
than dirty tricks(*1) to make sure that the instance implements a method 
which is called according to the interface type (and actually call it!).

The classes implementing these interface still must implement the 
methods (*once* for every class hierarchy build). But they don't need to 
have a common ancestor. (Using decorators you can employ those though).

*1: my best bet would be defining a common abstract class providing all 
methods in an abstract way, and the successors implementing this one 
partially. From the design pov this is not a good idea...

I hope this somewhat gives you an idea of the capabilites of this feature.
(Actually COM interfaces can be considered somewhat degenerate in that 
aspect)



Btw, also D8 supports it:
http://bdn.borland.com/article/0,1410,29779,00.html
I don't see a reason why there's an ambiguous call error in the example
(I think this is due to implementation restrictions/compatibility
issues) - after all there is only one implementation for ICombo available.
Btw, D8+ also supports "as" between two interfaces as seen from the example.
Regards,
  Thomas

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
http://lists.freepascal.org/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] "is"

2005-04-07 Thread Thomas Schatzl
Hello,
Marco van de Voort schrieb:
you could write:
x : TSomeElement;
someCollection : TSomeCollection;
[...]
foreach elem in someCollection do begin
<< do something with elem >>
end;
[...]
True, BUT... hmm, I actually have 3 BUTS
It is some sort of syntactic sugar, so all your but's are valid at least 
in some way. Nobody ever doubted this.

BUT 1: some form shorter syntax can be thought up for _each_ _and_
[...]
BUT 2: The whole purpose of iterators is that you can have multiple orders
on the same object, and get a different iterator to get a different order.
I disagree, the main purpose of iterators is to provide uniform 
iteration over containers (e.g. to use the same code to iterate over 
different collections).
That there are different types of iterators (forward, backward, and 
random access, readonly, writeable, ...) is definitely a feature of a 
specific iterator imo.

If you have to program differently for every container (for i := low(x) 
to ..; for i := 0 to count-1 ...; etc. etc.) only makes code harder to 
understand and error prone without additional gain.

Additionally if you examine code, _far_ the most applications do simple 
forward (and maybe backward) iteration over a collection (also because 
there are not that much collections where random access makes sense 
runtime-wise).

So maybe it is feasible to enhance the syntax to the following (BNF this 
time):

foreachstatement ::=
  "foreach" element "in" container ["forwards" | "backwards"] "do"
  statement.
or:
foreachstatement ::=
  "foreach" element "in" typed-range-expression "do"
  statement.
typed-range-expression ::=
  container "'" range-expression.
range-expression ::= first-element-spec ".." last-element-spec.
and first-element and last-element being a specification of those. This 
can be for arrays: bottom and last element (greetings from  e.g. 
VHDL/ADA?, see for more details there), for OO containers a 
specification of two iterators indicating position, see STL.
But this may be total overkill for the typical application.

*ducks into cover, awaiting flames* =)
The syntax does not allow this. Typical ss.
You are right, it _is_ syntactic sugar. Never doubted it. But iterators 
may be an important tool in programming which may justify inclusion into 
the language.

BUT 3: I myself currently use iterators at work (modeled after decal,
except without the variant-interface stuff, because of performance) and
your example looks overly verbose;
var iter:dlightmapiterator;
iter:=lightmapstartiter(collection);
while lightmapiterateover(iter) do
 << do something with getobject(iter)>>
// no finalisation of iter necessary.
 
Is this so bad ? I don't see the problem
I'm not sure that I'm interpreting your code sequence correctly, but I 
don't consider this design as very good either (maybe driven by 
implementation considerations though) - you lost abstraction over the 
container type which forces the programmer to learn the correct commands 
for every type of container (which isn't too hard assuming that they are 
pretty similar - but there is no real *gain* for doing that. And you 
don't lose anything when you do it the other way)

The finalization is a pure design issue, depending on the capabilities 
of the container (thread safety, safe for container modification, ...) 
which may be needed to properly release used resources. This is an option.

Additionally you lost type safety (e.g. missing the typecast of the 
retrieved object =).

Taking all that into account there isn't much difference in typing 
between the two proposals, isn't there?

which is imo far easier to read. (Please don't mind the word "foreach", 
I think it's only because it's already used in other languages for that 
purposes).
IMHO it sucks. Write a code template for your IDE if you really want to 
spare out those 10 key strokes.
:-)
Regards,
  Thomas

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
http://lists.freepascal.org/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] "is"

2005-04-07 Thread Thomas Schatzl
Hello,
Thomas Schatzl schrieb:
Michael Van Canneyt schrieb:
For example;
MI for interfaces for me is strange (to say the least) because
interfaces were introduced to avoid the mess of MI in the first
place. So why on earth would you want to introduce it ??
Since interfaces do not have/inherit implementations by definition this
is not an issue, they simply aggregate function *specifications* (no 
code). This is multiple _interface_ inheritance. Commonly considered 
good =)
Maybe an elaboration of the Rat-example (well, grew to a mouse-example 
:) helps to clarify some problems you have with interface-MI:

I marked the comments which talk about advantages this feature gives the 
programmer with "(+)"

type
TWasAcceptable = Boolean;
IMammal = interface
   procedure sleep(); // a mammal can sleep
end;
IMeatEater = interface
// a meat eater can eat
   function eat(m : TMeal) : TWasAcceptable;
end;
IPlantEater = interface
// a plant eater can eat too
function eat(m : TMeal) : TWasAcceptable;
end;
// something that can eat both plants and meat
IEatsEverything = interface(IMeatEater, IPlantEater);
IJumping = interface
   procedure jumparound();
end;
// a mouse can sleep, eat something and wiggle with its
// tail (and certainly much more =)
IMouse = interface(IMammal, IEatsEverything)
   procedure wiggleWithTail();
end;
Now I have several types of mice:
// a mouse can do everything what a mouse is supposed to do
// this class _implements_ the abilities
TMouse = class(TAnimal, IMouse)
   procedure sleep();
   procedure eat(m : TMeal);
   procedure wiggleWithTail();
end;
(+) Don't need to specify that TMouse implements IMammal, IPlantEater, 
IMeatEater and 10 other interfaces as well. I hope you don't forget one.
Can be "remedied" by a hierarchy of singly inherited interfaces, e.g. 
IPlantEatingMammal, IMeatEatingMammal, IAllEatingMammal, IMouseMammal, 
IJumpingMouseMammal, ...

// for example's sake although Micky Mouse looks like a mouse
// and can do the same things as the animal with the same
// name it is not derived from TMouse (it is not an animal
// after all)
TMickeyMouse = class(TCartoonCharacter, IMouse, IJumping)
   procedure sleep();
   procedure eat(m : TMeal);
   procedure wiggleWithTail();
   procedure jumparound();
end;
[...]
Implement methods of classes. Certainly in completely different ways - 
while the mouse animal accepts maybe rotten meat, Mickey Mouse may not, 
but it eats edible meals. Maybe due to the ancestors (single 
inheritance!) the methods are already partially implemented as well.
[...]

(+) You may notice that although there's a name clash in the interface 
specification. Does not matter at _all_, because it's only a 
specification, e.g. "has that method, don't care how it's implemented" 
(***).
(+) There's no problem with instantiation either because you simply do 
_not_ instantiate interfaces at all. It's only a specification of what 
this type can do.

In the code using these classes I can now use instances of both classes 
(no news here, you could that with single inherited interfaces too).

E.g.
// gives *any* plant eater a meal to eat. Consider we have some cow
// class which implements IPlantEater as well (and hence can eat too!)
procedure eat_a_plant(obj : IPlantEater; plant : TPlantMeal);
...
// gives the given object a meal. Since this is a generic meal, only
// objects which can eat everything can eat here. No need to check
// further in the code of the procedure whether the meal is of meat or
// plant type, we can be sure it accepts it.
procedure eat_just_something(obj : IEatsEverything; meal : TMeal);
...
(+)Can't do that with singly inherited interfaces... (in this case it 
might be reasonable to check whether the meal is edible for 
planteaters/meateaters and maybe throw an exception) but actually it'd 
be natural.
Consider the case when there's not only two types of things animal can 
eat... remember that case'ing doesn't work with interfaces.
(+) Again: The fact that IEatseverything is multiply inherited (and the 
method signatures overlap) does not even matter. Interfaces are just 
specifications which are always bound on runtime

// gives the mouse a meal and forces tail wiggling if it was
// acceptable
procedure eat_some_meat_and_wiggle_with_tail_if_ok(obj : IMouse; meat : 
TMeal);
...

// lets the given mammal walk around
procedure walkaround(obj : IMammal);
...
---
What happens if you add another completely different animal?
IFish = interface
procedure swim();
end;
IShark = interface(IFish, IEatsEverything);
TShark = class(TFish, IShark)
procedure swim();
procedure eat(m : TMeal);
end;
 any method (which were originally designed for the hierachy of 
mammals) still work. You can feed the shark independent of being a shark 
(only interested that it can eat everyth

Re: [fpc-pascal] "is"

2005-04-07 Thread Michael Van Canneyt


On Thu, 7 Apr 2005, Thomas Schatzl wrote:

> Hello,
> 
> Thomas Schatzl schrieb:
> > Michael Van Canneyt schrieb:
> > 
> > > For example;
> > > MI for interfaces for me is strange (to say the least) because
> > > interfaces were introduced to avoid the mess of MI in the first
> > > place. So why on earth would you want to introduce it ??
> > 
> > Since interfaces do not have/inherit implementations by definition this
> > is not an issue, they simply aggregate function *specifications* (no
> > code). This is multiple _interface_ inheritance. Commonly considered good
> > =)
> 
> Maybe an elaboration of the Rat-example (well, grew to a mouse-example 
> :) helps to clarify some problems you have with interface-MI:
> 
> I marked the comments which talk about advantages this feature gives the
> programmer with "(+)"
> 
> type
> TWasAcceptable = Boolean;
> 
> IMammal = interface
>   procedure sleep(); // a mammal can sleep
> end;
> IMeatEater = interface
> // a meat eater can eat
>   function eat(m : TMeal) : TWasAcceptable;
> end;
> IPlantEater = interface
>// a plant eater can eat too
>   function eat(m : TMeal) : TWasAcceptable;
> end;
> // something that can eat both plants and meat
> IEatsEverything = interface(IMeatEater, IPlantEater);
> 
> IJumping = interface
>   procedure jumparound();
> end;
> 
> // a mouse can sleep, eat something and wiggle with its
> // tail (and certainly much more =)
> IMouse = interface(IMammal, IEatsEverything)
>   procedure wiggleWithTail();
> end;
> 
> Now I have several types of mice:
> 
> // a mouse can do everything what a mouse is supposed to do
> // this class _implements_ the abilities
> TMouse = class(TAnimal, IMouse)
> procedure sleep();
> procedure eat(m : TMeal);
> procedure wiggleWithTail();
> end;
> 
> (+) Don't need to specify that TMouse implements IMammal, IPlantEater,
> IMeatEater and 10 other interfaces as well. I hope you don't forget one.
> Can be "remedied" by a hierarchy of singly inherited interfaces, e.g.
> IPlantEatingMammal, IMeatEatingMammal, IAllEatingMammal, IMouseMammal,
> IJumpingMouseMammal, ...
> 
> // for example's sake although Micky Mouse looks like a mouse
> // and can do the same things as the animal with the same
> // name it is not derived from TMouse (it is not an animal
> // after all)
> TMickeyMouse = class(TCartoonCharacter, IMouse, IJumping)
> procedure sleep();
> procedure eat(m : TMeal);
> procedure wiggleWithTail();
> procedure jumparound();
> end;
> 
> [...]
> Implement methods of classes. Certainly in completely different ways - while
> the mouse animal accepts maybe rotten meat, Mickey Mouse may not, but it eats
> edible meals. Maybe due to the ancestors (single inheritance!) the methods are
> already partially implemented as well.
> [...]
> 
> (+) You may notice that although there's a name clash in the interface
> specification. Does not matter at _all_, because it's only a specification,
> e.g. "has that method, don't care how it's implemented" (***).
> (+) There's no problem with instantiation either because you simply do _not_
> instantiate interfaces at all. It's only a specification of what this type can
> do.
> 
> In the code using these classes I can now use instances of both classes (no
> news here, you could that with single inherited interfaces too).
> 
> E.g.
> // gives *any* plant eater a meal to eat. Consider we have some cow
> // class which implements IPlantEater as well (and hence can eat too!)
> procedure eat_a_plant(obj : IPlantEater; plant : TPlantMeal);
> ...
> 
> // gives the given object a meal. Since this is a generic meal, only
> // objects which can eat everything can eat here. No need to check
> // further in the code of the procedure whether the meal is of meat or
> // plant type, we can be sure it accepts it.
> procedure eat_just_something(obj : IEatsEverything; meal : TMeal);
> ...
> 
> (+)Can't do that with singly inherited interfaces... (in this case it might be
> reasonable to check whether the meal is edible for planteaters/meateaters and
> maybe throw an exception) but actually it'd be natural.
> Consider the case when there's not only two types of things animal can eat...
> remember that case'ing doesn't work with interfaces.
> (+) Again: The fact that IEatseverything is multiply inherited (and the method
> signatures overlap) does not even matter. Interfaces are just specifications
> which are always bound on runtime
> 
> // gives the mouse a meal and forces tail wiggling if it was
> // acceptable
> procedure eat_some_meat_and_wiggle_with_tail_if_ok(obj : IMouse; meat :
> TMeal);
> ...
> 
> // lets the given mammal walk around
> procedure walkaround(obj : IMammal);
> ...
> 
> ---
> What happens if you add another completely different animal?
> 
> IFish = interface
>   procedure swim();
> end;
> 
> IShark = interface(IFish, IEatsEverything);
> 
> TShark = class(TFish, IShark)
> procedure swim();
> procedure eat(m : TMeal);
> end;
> 
>  any method (which were 

Re: [fpc-pascal] "is"

2005-04-07 Thread Florian Klaempfl
Thomas Schatzl wrote:
> 
> Additionally if you examine code, _far_ the most applications do simple
> forward (and maybe backward) iteration over a collection (also because
> there are not that much collections where random access makes sense
> runtime-wise).

There come a lot in my mind: e.g. containers using internally trees
there "pre node" processing is cachewise usefull, "spare" sets,
containers using linked list to represent data internally etc.

> 
> So maybe it is feasible to enhance the syntax to the following (BNF this
> time):
> 
> foreachstatement ::=
>   "foreach" element "in" container ["forwards" | "backwards"] "do"
>   statement.
> 
> or:
> 
> foreachstatement ::=
>   "foreach" element "in" typed-range-expression "do"
>   statement.
> 
> typed-range-expression ::=
>   container "'" range-expression.
> 
> range-expression ::= first-element-spec ".." last-element-spec.
> 
> and first-element and last-element being a specification of those. This
> can be for arrays: bottom and last element (greetings from  e.g.
> VHDL/ADA?, see for more details there), for OO containers a
> specification of two iterators indicating position, see STL.
> But this may be total overkill for the typical application.
> 
> *ducks into cover, awaiting flames* =)

No, these ideas I like much more than a plain foreach statement. Though
I think implementing this using interfaces makes no use for me because
usually interfaces pulls in too much overhead so it doesn't scale very
well. Just look at the compiler, there are a lot of linked lists used like:

p:=first;
while assigned(p) do
  begin
...
p:=p^.next;
  end;

while the list in a lot of cases has only one element.

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
http://lists.freepascal.org/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] "is"

2005-04-07 Thread Marco van de Voort
> >><< do something with elem >>
> >>end;
> >>[...]
> > 
> > True, BUT... hmm, I actually have 3 BUTS
> 
> It is some sort of syntactic sugar, so all your but's are valid at least 
> in some way. Nobody ever doubted this.

Good.
 
> > BUT 1: some form shorter syntax can be thought up for _each_ _and_
> > [...]
> > 
> > BUT 2: The whole purpose of iterators is that you can have multiple orders
> >on the same object, and get a different iterator to get a different order.
> 
> I disagree, the main purpose of iterators is to provide uniform 
> iteration over containers (e.g. to use the same code to iterate over 
> different collections).
> That there are different types of iterators (forward, backward, and 
> random access, readonly, writeable, ...) is definitely a feature of a 
> specific iterator imo.

I'm more thinking on different properties. E.g. on address, on name etc. 

> If you have to program differently for every container (for i := low(x) 
> to ..; for i := 0 to count-1 ...; etc. etc.) only makes code harder to 
> understand and error prone without additional gain.

I don't buy that kind of arguments. This is Pascal, not Matlab. You
need to have a general idea about is what happening, and knowledge about
typing is a fundament of a strongly typed language. Note that I name Matlab
and not VB. Contrary to popular myth, you need to have a pretty good sense
about what is happening in VB too, at least if your program is non-trivial.
 
> Additionally if you examine code, _far_ the most applications do simple 
> forward (and maybe backward) iteration over a collection (also because 
> there are not that much collections where random access makes sense 
> runtime-wise).

My point was that if you invent syntax, at least make it somewhat general.
However that is pretty much the point of this extension. A few people knew
this construct from VB, which has no sane syntax anyway, and complained
about the two extra keystrokes. It has nothing to do with design or thinking
about a nice, general solution.
 
> So maybe it is feasible to enhance the syntax to the following (BNF this 
> time):
> 
> foreachstatement ::=
>"foreach" element "in" container ["forwards" | "backwards"] "do"
>statement.

foreach element in container[.interface] do
  statement

with interface the a property that returns an interface to the iterator,
one being default 

However this is all just trying to find justification to implement it. I
brought the point up to show that the syntax wasn't really thought about,
just plain copied.
 
> *ducks into cover, awaiting flames* =)

IMHO this time to find arguments to justify something stupid as for..each
and MI interfaces should better be invested in researching a documenting a
possible implementation for generics. At least that opens a scala in
possibilities, and is not simply trying to avoid two keystrokes of typing,
that could be solved with an IDE macro/template anyway.
 
> You are right, it _is_ syntactic sugar. Never doubted it. But iterators 
> may be an important tool in programming which may justify inclusion into 
> the language.

Then come with a nice library design that does iterators in a sane way. With
possible extensions. Then we might have a sane talk about it.
 
> > while lightmapiterateover(iter) do
> >  << do something with lightmapgetobject(iter)>>
> > // no finalisation of iter necessary.
> >  
> > Is this so bad ? I don't see the problem
> 
> I'm not sure that I'm interpreting your code sequence correctly, but I 
> don't consider this design as very good either (maybe driven by 
> implementation considerations though) - you lost abstraction over the 
> container type which forces the programmer to learn the correct commands 
> for every type of container (which isn't too hard assuming that they are 
> pretty similar - but there is no real *gain* for doing that. And you 
> don't lose anything when you do it the other way)

You might notice that the names are exactly DECAL notation with type
prefixed. However decal (an interface based implementation) was painfully
slow. A decal  map is slower than a sorted tstringlist,
which is already not really a performance wonder.

> The finalization is a pure design issue, depending on the capabilities 
> of the container (thread safety, safe for container modification, ...) 
> which may be needed to properly release used resources. This is an option.

Indeed.
 
> Additionally you lost type safety (e.g. missing the typecast of the 
> retrieved object =).

There is only one solution for that. Generics. Not a custom solution for
every point were it might occur.

> Taking all that into account there isn't much difference in typing 
> between the two proposals, isn't there?

IMHO not in all three, since it is all invoking a template to me :)
 

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
http://lists.freepascal.org/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] "is"

2005-04-07 Thread Thomas Schatzl
Hello,
Marco van de Voort schrieb:
That there are different types of iterators (forward, backward, and 
random access, readonly, writeable, ...) is definitely a feature of a 
specific iterator imo.
I'm more thinking on different properties. E.g. on address, on name etc. 
I don't understand that remark right now, but it sounds interesting 
(maybe because it's late). Care to explain that briefly? (Or give a 
reference)

I don't buy that kind of arguments. This is Pascal, not Matlab. You
need to have a general idea about is what happening, and knowledge about
typing is a fundament of a strongly typed language.
I don't agree to this argument that this needs to reflect in the syntax 
(your argument is "this is Pascal and that's it"). The type of a 
container is the specification after the double colon, not the loop 
structure/syntax. It does not disallow the traditional way either; all 
components used (container, element) in this syntax are properly typed, 
and it does not weaken typing at all.

My point was that if you invent syntax, at least make it somewhat general.
However that is pretty much the point of this extension. A few people knew
this construct from VB, which has no sane syntax anyway, and complained
about the two extra keystrokes. It has nothing to do with design or thinking
about a nice, general solution.
and
*ducks into cover, awaiting flames* =)
IMHO this time to find arguments to justify something stupid as for..each
You are right in these aspects; I'm not the person who cares much about 
that "foreach" either. That means that I can happily live without it and 
don't care about this syntax which is lacking usefulness, so I accept 
and actually agree to this decision in the end.

Regards,
  Thomas
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
http://lists.freepascal.org/mailman/listinfo/fpc-pascal