Interestingly, my background is close to that of Walter: I also
developed video games for a living, I also worked on a commercial C++
implementation, and I also designed my own language, called LX. More
information on LX can be found at http://mozart-dev.sf.net.
So I am going to start a big series of post to try to compare D and LX.
To avoid polluting the newsgroup too much, I am going to try to post all
of them under the same thread.
I am obviously biased. On the other hand, I believe there is much to be
gained if we exchange ideas and compare designs. Please bear with me...
Christophe

1/ Macros
I do believe in the necessity of macros. Because it is simple technology
doesn't mean it is obsolete (unlike "register ;-) It is obsolete only for
modularization (#include). But there is still no better way for
environment-dependent compilation (#ifdef), or for textual replacements
(see my extensive use of '.tbl' files in the LX compiler). Back in the
1990's, I worked on the Alsys Ada compiler, which had a hidden preprocessor
for internal use (#ifdefs), disabled on customer versions since that was
not part of the Ada spec. I always
thought this was silly :-)
Your examples did not convince me either: you take seleted examples of
macro applications (for example dealing with different compilers), and then
say "it's useless in D". Well, then, you have to make sure that D
is absolutely perfectly portable. I hope you covered the various cases of
machine idioms that I documented in
http://home.earthlink.net/~descubes/C-- and in particular, that you have a
D-defined identifier for everything that has possibly ever been put in an
autoconf file :-)
Another point is that, if you don't have a standard preprocessor, then your
customers will use another one. I have this very problem with LX, because
one part of its syntax makes it incompatible with the C preprocessor (LX
numbers like 16#FFFF), so I need to recommend another preprocessor like m4,
which is quite complicated...
Christophe

"Christophe de Dinechin" <descubes earthlink.net> wrote in message
news:3B7C8D0B.DC4770AD earthlink.net...

1/ Macros
I do believe in the necessity of macros. Because it is simple technology
doesn't mean it is obsolete (unlike "register ;-) It is obsolete only for
modularization (#include). But there is still no better way for
environment-dependent compilation (#ifdef), or for textual replacements
(see my extensive use of '.tbl' files in the LX compiler).

I agree with this viewpoint. Macros are necessary.
The D spec document pointed the problem with macros, they go through all
scopes!
That is a good insight. Let's keep it. We need then SCOPED macros!
A macro should be defined in a scope and should take typed arguments.
A template is just a macro. The template discussion should solve this.
Templates ARE macros. I will post later what I mean exactly with that, and a
template spec.

Many confuse conditional compilation with macro's, that are actually two
completly different things, in C they are handled by the same instance, but
a language can either implement only one of the two, or handle them both
with different instances. In my eyes conditional compiliation is a must,
Macros are not.

2/ Why D?
When I was slashdotted on LX, I got one major feedback: you need to explain
why people would need to use your language, what it does that C++ or Java
can't do. Curiously, ease of use or implementation is a concern only to a
microscopic minority. Even the '10% productivity increase' claim you make
is probably difficult to sell. Hey, Perl is a successful language!
In the case of LX, the objective is to make a compiled, yet extensible
programming language. There are many things you can write in LX easily that
you can absolutely not write in C++ or any other existing language. See
some examples below (in future posts), or on
http://mozart-dev.sf.net/lx.html.
This doesn't mean that simplicity and ease of implementation /
specification are not important. They were the starting point for LX
initially. But they are now only secondary objective, and I "sell" LX
differently than I used to.
Christophe

3/ C source code compatibility
If you decide to drop it, you may as well go the whole way. LX, for
instance, has an indentation based syntax with a lot of optional
punctuation that makes it much more concise than C or C++ in many common
cases, even though it appears more verbose at first. My gut feeling there
is that D is only a marginal, incremental improvement over C or Java,
whereas LX is a radical change (for the best, I hope). But of course, I am
very biased...
Maybe it's time to give an example:
import IO = LX.Text_IO
-- Validated "template" type: "integer" passes the test, "object" doesn't
generic type ordered if
with ordered A, B
with boolean C := A < B
-- This function is implicitly template because "ordered" is.
function Min(ordered A) return ordered is
return A
-- This function takes a variable number of arguments
function Min(ordered A, others) return ordered is
with ordered B := Min(others)
if A < B then return A; else return B
procedure TestIt() is
with integer X := Min(5, 4, 2, 3, 1, 5)
with real Y := Min(1.4, 7.0, 2.9)
IO.WriteLn "X=", X, Y format "Y=#5.9#"
Christophe

4/ Multiple inheritance:
I think you did almost the right thing: dumping implementation-side
multiple inheritance, while keeping it at the interface side. But here too,
I believe LX goes one step further, by also decoupling interface
inheritance and implementation inheritance for the single-inheritance case:
-- Interface inheritance
type large_integer like integer
function Foo(integer I) return integer
function Bar(large_integer L) return integer is
return Foo(L) -- OK
-- Implementation inheritance. "record" is an empty base type
type point_2D is record with
coordinate X, Y
type point_3D is point_2D with
coordinate Z
What this gives you is more flexibility for future evolution of the
software, because implementation and interface are no longer so tied
together.
Christophe

5/ Templates
You say that you are looking for a solution. Look at the LX generic types,
I'm sure they will give you interesting ideas, notably implicitly template
types ("ordered" above). Any template type like array also makes functions
that use it implicitly template, which reduces the code clutter in things
like the STL dramatically. LX generics can be parameterized with any kind
of object, not just integer/types (see the parameterization of array with a
range argument below).
generic [type value] type range written range of value is
record with
value low, high
function range(range.value low, high) return range written low..high is
result.low := low
result.high := high
generic [range index; type item] type array
written array[index] of item
function Min(array A of ordered, others) return ordered is
result := Min(others)
with array.item I
for I in A loop
result := Min(result, I)
Template validation (see ordered) and predicated template specialization
also simplify many things:
-- C++ style full specialization
generic type array for array[1..5, integer]
-- C++ style partial specialization
generic [range index; type item] type array for array [index,
pointer[item]]
-- Predicate-based specialization
generic type array when size(array.item) = size(integer)
Granted, some of this is a bit difficult to implement, and my LX compiler
is only halfway through it... But I'm making good progress already.
Previous posts suggested the Ada or Eiffel generics model. There was one
major difference between Ada and C++: implicit instantiation. In Ada or
Eiffel, you have to explicitly instantiate everything. This basically makes
the STL impossible to implement.
Christophe

Christophe de Dinechin wrote in message <3B7C8E79.109F4270 earthlink.net>...
...
I like the explicit instantiation. Since there is an STL workalike for Ada,
which I haven't looked at, somebody must have dealt with this problem.

But in an earlier thread it was claimed that STL for Ada was 10 times as
large as the equivalent STL for C++. So the dealing with it may not
have been easy.

But in an earlier thread it was claimed that STL for Ada was 10 times as
large as the equivalent STL for C++. So the dealing with it may not
have been easy.

Large in source or large in object?

Source.
"Rational ships a C++ version of The Booch Components that was
originally designed for and implemented in Ada by Grady Booch. ...
The Ada version is 125,000 non-commented source lines compared
to the C++ version's 10,000 lines -- inheritance combined with
templates can be a very powerful mechanism for organizing
libraries without loss of performance or clarity."
-Stroustrup in D&EoC++, citing Booch's _Object Oriented
Analysis and Design with Applications, 2nd edition"
-RB

But in an earlier thread it was claimed that STL for Ada was 10 times as
large as the equivalent STL for C++. So the dealing with it may not
have been easy.

Large in source or large in object?

Source.
"Rational ships a C++ version of The Booch Components that was
originally designed for and implemented in Ada by Grady Booch. ...
The Ada version is 125,000 non-commented source lines compared
to the C++ version's 10,000 lines -- inheritance combined with
templates can be a very powerful mechanism for organizing
libraries without loss of performance or clarity."
-Stroustrup in D&EoC++, citing Booch's _Object Oriented
Analysis and Design with Applications, 2nd edition"
-RB

But I think that Eiffel generics give an equivalent amount of
compression. And they are certainly easier to learn and to use.
Thus:
Tree[L->Comparable] -- L is an arbitrary choice,
-- Comparable is a class defined elsewhere
... -- every time this class is instantiated, it
-- must have a class that implements Comparable
feature
root : TreeNode[L]; -- all class references are pointers
sentinal : TreeNode[L]; -- or handles, no pointer notation
... -- needed(or even available);
end;
If you want to specify a non-handle/pointer class, you define it in the
class type as an "expanded" class. If you want an abstract class, you
define it in the class type as a "deferred" class.
Then when you want to create an instance of a Tree, you say something like:
thisTree : Tree[String];
and the tree will be ordered in the same way that String is ordered.
Sometimes I find this a bit confining, but it is easy to learn, and
seems to do the job. Since everything is done via a reference, sizes
are known at compile time. (Expanded classes are special here, of
course, but they also have special limitations that allow them to be
handled easily.)
OTOH, for basic stuff, and for tricky stuff, the class library
implementors all fall back into C. Not that bad an idea, but it means
that C interoperability should have been a higher priority on the
language designer's list of features. As it was, this is always tacked
on later. And shows it.

OTOH, for basic stuff, and for tricky stuff, the class library
implementors all fall back into C. Not that bad an idea, but it means
that C interoperability should have been a higher priority on the
language designer's list of features. As it was, this is always tacked
on later. And shows it.

OTOH, for basic stuff, and for tricky stuff, the class library
implementors all fall back into C. Not that bad an idea, but it means
that C interoperability should have been a higher priority on the
language designer's list of features. As it was, this is always tacked
on later. And shows it.

This sounds like a serious problem with Eiffel.

This is a problem. As to serious... well, that sort of depends on what
you're doing. Also, this is implementation specific (i.e., not a part
of the language specifications), so it's quite possible that some
implementations have done it better than the ones that I'm more familiar
with. And usually one doesn't need to go there. But it is a problem.
And sometimes it can be serious (though not grave, just exceptionally
irritating, and requiring lots of extra care to get everything just right).

6/ Creating objects on the stack
You say that in D, all objects are by reference. This makes "integer"
different than objects, and that was a compromise I was not willing to make
for LX. Instead, "integer" is not a built-in type any more than any
user-defined object type. Also, for several types (notably simple template
types), having them stack-based makes them much more efficient.
-- I want this to live on the stack.
type complex is record with
real Re, Im
Using LX pragmas, it is possible to define on a per-type basis how the
compiler accesses it. Among the default pragmas are the {dynamic} pragma
that specifies that a type resides in garbage collected memory, and thus is
always referenced to. At the root of the LX type hierarchy is "record" (and
"module", which is just "constant record"). Right above it is "object",
which is just "record" with the {dynamic} pragma. So anything that derives
from "object" is dynamic.
-- I want this to reside in garbage-collected memory
type tree is object with
tree left, right
function tree(tree left, right) return tree is
-- Implicit allocation occurs here.
result.left := left
result.right := right
-- I want to do this with a stack based object as a root:
{dynamic} type complex_tree is complex with
complex_tree left, right
Christophe

7/ Trigraphs:
Full agreement here. The current LX specification says that an LX compiler
must accept, in preferred order: Unicode source, ASCII source, proprietary
character set, and that if the native charset is proprietary, then an
import and export procedure to Unicode and ASCII must be provided.
However, I disagree with the runtime types. You are asking questions about
ascii or unicode or char. Well, if these were not basic types, there would
be no need to ask. So in LX, the LIBRARY will define the following:
- A generic "character" type
- A char type, which is a specialization of character for the most
common char representation on the machine
- Modules for Unicode, ASCII and EBCDIC encodings that offer:
- a basic type for representing the chars in memory (ascii, unicode,
etc), which is a specialization of character
- conversion routines to generic char types, I/O, etc. They also
contain declarations for things like: ASCII.CR, ASCII.LF, etc.
Something I will repeat like a mantra in my posts: built-in types are EVIL.
Yucky. Don't do that. Keep clear. _That_ is obsolete :-)
Christophe

8/ Operator overloading
Total disagreement here :-) Operator overloading is at the very core of the
LX syntax, but it takes a completely different approach than C++ (the
'written' keyword that you may have seen in previous posts already).
Written allows for N-ary operator overloading, and also the definition of
new infix named operators and implicit conversions:
function And(integer A, B) return integer written A and B
function MultiplyAndAdd(matrix A, B, C) return matrix written A*B+C
function real(integer N) return real written N
Without operator overloading, you miss big time on most scientific
applications... What is necessary is a much simplified set of lookup and
implicit conversion rules, which LX provides (it fits on a page, literally)
I also disagree that you can't use operator overloading to create libraries
that work. Blitz++ is a good counter example.
Christophe

"Christophe de Dinechin" <descubes earthlink.net> wrote in message
news:3B7C905D.5CFA76BC earthlink.net...

8/ Operator overloading
Total disagreement here :-) Operator overloading is at the very core of

the

LX syntax, but it takes a completely different approach than C++ (the
'written' keyword that you may have seen in previous posts already).
Written allows for N-ary operator overloading, and also the definition of
new infix named operators and implicit conversions:
function And(integer A, B) return integer written A and B
function MultiplyAndAdd(matrix A, B, C) return matrix written A*B+C
function real(integer N) return real written N

Is this implemented?
Is (A*B)+C also equal to MultiplyAndAdd?
And (Horror)
Is A*(B+C) ALSO equal to MultiplyAndAdd?
How about parentheses?

"Christophe de Dinechin" <descubes earthlink.net> wrote in message
news:3B7C905D.5CFA76BC earthlink.net...

8/ Operator overloading
Total disagreement here :-) Operator overloading is at the very core of

the

LX syntax, but it takes a completely different approach than C++ (the
'written' keyword that you may have seen in previous posts already).
Written allows for N-ary operator overloading, and also the definition of
new infix named operators and implicit conversions:
function And(integer A, B) return integer written A and B
function MultiplyAndAdd(matrix A, B, C) return matrix written A*B+C
function real(integer N) return real written N

Is this implemented?
Is (A*B)+C also equal to MultiplyAndAdd?
And (Horror)
Is A*(B+C) ALSO equal to MultiplyAndAdd?
How about parentheses?

10/ Bit fields of arbitrary size
As other posters noted, there are cases where bit fields are just
absolutely required. LX allows bit fields, again using predefined pragmas.
I used to work a lot on real-time applications, and, no, sorry, bitmasking
and shifting was not really an option.
{volatile} {align 16} {memory_width 16} {memory_space IO}
type chip_control_register is record with
integer command {bit_offset 0} {bit_size 3}
integer size {bit_offset 4} {bit_size 4}
boolean ready {bit_offset 8} {bit_size 1}
boolean error {bit_offset 9} {bit_size 1}
{address 16#FFFF_ED00} chip_control_register chip_A
{address 16#FFFF_ED04} chip_control_register chip_B
{address 16#FFFF_ED08} chip_control_register chip_C
By the way, the statement that "bitmasking is better because it generates
better code" is a very x86-centric view of the universe. The PA-RISC and
IA-64 have "extract" and "deposit" instructions, and the compilers are much
better at finding those on bit field accesses than on bitmask and shifting
(at least at lower optimization levels)
Christophe

11/ Support for 16-bit computers
I believe what you really meant was: support for the 16-bit segmented
memory model on the x86 architecture.
LX pragmas also offer a nice answer there. Should you need near/far
pointers, which may happen on some 64-bit architectures (where near means
32-bit, and far 64-bit):
{near} type near_int_pointer is pointer to integer
{far} type far_int_pointer is pointer to integer
Regarding more modern uses of {near} and {far}. On IA-64 on HP-UX, there is
a 32-bit memory model (for better source compatibility with 32-bit
applications). Internally, all pointers are really 64-bit, however, they
are converted when used. And, guess what, there are cases where you need a
64-bit pointer from a 32-bit application :-) Not that HP implemented a _far
keyword, for that matter...
Note that there is also a standard pragma (bit_size, seen above) that might
fit the bill:
{bit_size 16} type near_int_pointer is pointer to integer
{bit_size 32} type far_int_pointer is pointer to integer
but to be exhaustive, near and far convey more information than just bit
size.
Christophe

12/ RTTI
LX uses "reflection" rather than RTTI, and the full variant of it (not
introspection as in Java). The LX runtime environment that supports it is
independent (called Mozart). The key connection between reflection and LX
sources are LX pragmas that we already saw a couple of times. When the LX
compiler parses:
{glop} function foo() is
blah; bloh; blih
it looks for a pragma handler for {glop}, and passes it a standardized
tree representation for function foo. The pragma handler returns what foo
should be replaced with. In the future, pragma handlers will possibly be in
separate DLL. In any event, the intent is that the user can add their own
pragmas. Pragmas can be invoked in a number of phases of the compiler:
after parsing, before or after semantics, on references to the object (for
pragmas that apply to declarations), before or after expansion, or before
or after code generation, or as part of the optimization phase.
With reflection, you can implement: persistence, garbage collection, policy
control, automatic debugging code generation, etc... See
http://mozart-dev.sf.net.
Anyway, you talk somewhere about: How do I keep an enum and a char array in
sync. Well, if I had a preprocessor, here is what I would do:
// file.tbl
ZNORT(First)
ZNORT(Second)
ZNORT(Third)
// file.c
enum Znort {
#define ZNORT(x) x,
#include "file.tbl"
#undef ZNORT
Last
}
char *Znort_names[] = {
#define ZNORT(x) #x,
#include "file.tbl"
#undef ZNORT
NULL
}
And, in case you wonder, I do use this technique extensively in the LX
compiler to keep track of things like tokens, predefined strings, run-time
tables, etc.
Now, let's assume you don't have macros. That's where reflection RULES. It
makes things simpler to read for the end user, but the process is a bit
more dirty.
import CLX = LX.Compiler
{define_pragma named_enum before_semantics}
procedure NamedEnumPragma(CLX.context C; CLX.tree T) is
using CLX
{inline} procedure Error(string S) is
CLX.Error C, Position(T), S
with enum_type ET := T as enum_type
if ET = nil then Error "Not an enum"
-- Declare a temporary array, with the enum as an index
with declaration ArrayDecl := declaration(
name: tempname("enum"),
type: quote(array[low..high] of string)
initializer: nil)
-- Replace "low" and "high" names in the above quote
-- with the enum first and last
with expression low := quote(X.low)
replace low, quote(X), T -- Turn that into <enumtype>.low
with expression high := quote(X.high)
replace high, quote(X), T
replace ArrayDecl, quote(low), low
replace ArrayDecl, quote(high), high
-- Create the initializer. Don't use 'quote' for a change
with string Args of expression
with enumerator E
for E in ET.enumerators loop
Args &= E.name
with initializer Init := call(
callee: name("array"),
arguments: Args)
ArrayDecl.initializer := Init
-- Now create a function that allows Name(E) and E.name
with declaration FDecl :=
quote(function Name(E arg) return string
written E.name is
return ArrayDecl[arg]);
replace FDecl, quote(E), ET
-- Finally, append the two declarations just after the enum type decl
InsertDeclaration context: C, after: T, declaration: ArrayDecl
InsertDeclaration context: C, after: ArrayDecl, declaration: FDecl
-- OK, that was hard. Now for the fun part: see use of {named_decl}
procedure Test() is
with type X is {named_decl} enum(A, B, C, D)
-- Should output AB
IO.WriteLn Name(A), B.name
Christophe

Christophe de Dinechin wrote in message <3B7C932F.6E190162 earthlink.net>...

Anyway, you talk somewhere about: How do I keep an enum and a char array in
sync. Well, if I had a preprocessor, here is what I would do:

That's similar to a trick I use. For example, suppose I need an enum value
and string:
#define colors \
X(red) \
X(green) \
X(blue)
// Do the enum...
#define X(c) COLOR_##c,
enum COLOR { colors };
#undef X
// Do the corresponding array of strings...
#define X(c) #color ,
char color_strings[] = { colors };
#undef X
It's ugly, but I don't get the out-of-sync bugs anymore with it. This trick
works with the Digital Mars compiler which works with unlimited size macros.
Many C compilers fall over on this because they limit macro expansion text
size.
For more complex tables, I'll write a separate program that generates C
source which is then #Include'd.

13/ Garbage collection
LX supports it, but it is a side effect of well-defined pragmas applied to
selected types, rather than built-in into the language. The {dynamic}
pragma indicates that an object resides on the garbage collected heap.
"object" and "record" differ only in that "record" can live on the stack,
while "object" is always on the GC heap.
By the way, the LX compiler itself, although written in C++, uses a
mark-and-sweep garbage collector that uses reflective information generated
from C++ source :-)
Christophe

14/ Declaration vs. Definition - Modularity aspect
Good modularity sometimes imply that you don't make the implementation
details public. LX has clearly separate interface and implementation, with
a 'body' keyword that allows the kind of repetitions required in C++.
-- Interface, in file fact.lxs
function factorial(integer N) return integer
-- Body, in file fact.lxb. 'body' replaces all arguments
function factorial body is
...
For completeness, let me also indicate that what LX allows you do to with
any object is what the interface says, regardless of whether you see the
implementation. No 'private' parts. The exception is for types, where
knowing the implementation gives you access to it.
-- Type interface
type complex with
real Re, Im
out real Rho, Theta
-- This is legal just seeing the above
function Re(complex Z) return real is
return Z.Re
-- Type definition (possibly another file)
-- Notice there is no Rho and Theta fields
type complex body is
real Re, Im
-- So we need to tell the compiler what to do when the user does Z.Rho
function Rho(complex Z) return real written Z.Rho is
return Math.sqrt(Z.Re^2 + Z.Im^2)
Christophe

This is where I disagree.
The compiler doesn't need a separate interface and implementation. It
should be able to find the whole declaration (in D, the declaration and
definition are the same thing) and figure out what the public, accessible
parts are (the interface)
If you link in a library, you don't even need the source as long as you have
parse info built into the .obj files. This is how Java and C# and most
modern languages work. It saves the programmer the tedium of maintaining
two different parts of the source that say the exact same thing.
If one were inclined, the IDE could extract the parse info from the .obj
files and show the programmer a trimmed down, publics-only source file
automatically, on request. Or the IDE class browser could have an option to
hide everything but publics. Or the library author could include such a
publics-only, no-implementation source file for distribution, merely for
programmer reference purposes because the compiler wouldn't need it.
Question for Walter: If there were such a thing as forward declaration in
D, what would the syntax be? This is only so I could write such a IDE
browser tool that showed functions in a standard way.
Sean
"Christophe de Dinechin" <descubes earthlink.net> wrote in message
news:3B7C9418.23275491 earthlink.net...

14/ Declaration vs. Definition - Modularity aspect
Good modularity sometimes imply that you don't make the implementation
details public. LX has clearly separate interface and implementation, with
a 'body' keyword that allows the kind of repetitions required in C++.
-- Interface, in file fact.lxs
function factorial(integer N) return integer
-- Body, in file fact.lxb. 'body' replaces all arguments
function factorial body is
...
For completeness, let me also indicate that what LX allows you do to with
any object is what the interface says, regardless of whether you see the
implementation. No 'private' parts. The exception is for types, where
knowing the implementation gives you access to it.
-- Type interface
type complex with
real Re, Im
out real Rho, Theta
-- This is legal just seeing the above
function Re(complex Z) return real is
return Z.Re
-- Type definition (possibly another file)
-- Notice there is no Rho and Theta fields
type complex body is
real Re, Im
-- So we need to tell the compiler what to do when the user does Z.Rho
function Rho(complex Z) return real written Z.Rho is
return Math.sqrt(Z.Re^2 + Z.Im^2)
Christophe

15/ Declaration vs. Definition - Extensibility aspect
Your model of class definition doesn't allow one to extend a base class
(without deriving it), which in practice is a severe weakness in large
projects. Objective C is the only other compiled language I know that fixes
the problem (with categories). What LX does is giving up the notion of
"member function" (although you can recreate the X.f() notation with a
written form where suitable).
Ah ah, but what about dynamic dispatch, then? What LX has is the concept of
dynamic object, which is essentially a (type, reference) pair. This is
introduced with the any keyword. It allows C++ style dynamic dispatch with
two major extensions: a/ You can add "virtual" functions to a base type at
any point in the program, not just in the class interface. Thus, any class
remains extensible. b/ Dynamic dispatch can apply to more than one argument
at a time.
type shape
type rectangle like shape
type type circle like shape
-- "Virtual" function, and overrides for the various classes
-- There is no single location where these have to be.
-- Obviously, this requires a 'bind' phase to generate the vtables
-- from the whole program.
procedure Draw(any shape S) written S.Draw()
procedure Draw(any rectangle S) written S.Draw()
procedure Draw(any circle S) written S.Draw
-- Multi-way dynamic dispatch
function Intersect(any shape S1, S2) return any shape
function Intersect(any rectangle S1, S2) return any rectangle
function Intersect(any circle S1, S2) return any shape
Again, the major point here is that the "class" shape can be extended from
wherever in the program. This doesn't break encapsulation, however, because
the extensions can only see whatever interface is visible to them. In
particular, the body of the shape type is not visible to the extensions.
Christophe

17/ Modules - Importing
There are two orthogonal aspects that are commonly called "modules": 1/
encapsulating related stuff, and 2/ making declarations available across
translation units.
The second aspect of modules, communication across translation units, is
handled in LX as follows. Any translation unit is made of a specification
(foo.lxs) and an optional body (foo.lxb). Anything declared in a
specification file can be imported from another source using import. For
instance, we saw above:
import IO = LX.Text_IO
The "IO =" part is optional, but is useful to help shorten references to a
specific import without making the import globally visible. Without it, you
have to write LX.Text_IO.WriteLn. With it, you can write IO.WriteLn and it
does the same thing.
You can import any globally declared entity in a specification.
Christophe

18/ Modules - Shortening references
When using modules, it is also important to avoid polluting the namespace.
That really matters for "million of lines of code" projects. I see from the
Sieve example the "import stdio" in D implicitly imports printf, and I
believe it is bad. LX still allows you to shorten references (make them
implicit) with the "using" keyword.
An "using" statement adds an entity to the current context's map (it comes
last in the lookups for that context). For instance:
function Mod(in out array A of complex) return real
with array.index.value I
result := 0.0
for I in A.range loop
using A[I]
-- All references are implicitly to A[I]
result += Re * Re + Im * Im
result := Math.sqrt(result)
Now, remember that modules are simply constant records? This allows one to
say:
using IO
WriteLn "Hello world"
Christophe

19/ Modules - Child modules
One of the toughest aspects of Ada-style modularity is how can modules be
extended safely. LX borrows from Ada the notion of child package, although
it is implemented slightly differently.
Essentially, what this means is that there is a "master module" LX, which
is extended (with knowlege of the internal details of module LX) by a
module named LX.Text_IO, which itself is extended (with knowledge of the
internal details of module LX.Text_IO) by a module named
LX.Text_IO.Formatting
This system allows you to structure your software like russian dolls, with
each inner doll visible from the outside when you open the larger doll, but
also, while inside the larger doll, seing the inside of the larger doll. In
other words, you don't break encapsulation, but you preserve extensibility.
Christophe

19/ Real typedefs
While having 'handle' being separate is sometimes useful, it is also
sometimes an impediment (unless you also have conversion rules from a
typedef to the typedefed type, and implicit conversion headaches rear their
ugly heads). So for LX, I decided to make the creation of a new type not
the default, but an option.
type coordinate is real
coordinate C := 1.0 -- OK
function F(real R) return real
function F(coordinate C) return coordinate -- Error
type distance is other real
type time is other real
type speed is other real
function Div(distance D; time T) return speed written D/T -- OK
LATER NOTE: I just discovered you have typealias. OK, let me suggest that
typedef should behave the C way (as typealias does today), and that typenew
implements the new behavior...?. Don't change the meaning of typedef
gratuitously...
PS: does your syntax allow: "int typedef handle" as in C?
Christophe

Christophe de Dinechin wrote in message <3B7C964C.81944DE9 earthlink.net>...

LATER NOTE: I just discovered you have typealias. OK, let me suggest that
typedef should behave the C way (as typealias does today), and that typenew
implements the new behavior...?. Don't change the meaning of typedef
gratuitously...

Yes, that is a problem with D right now. I've been thinking of changing the
typealias keyword to simply alias.

PS: does your syntax allow: "int typedef handle" as in C?

No. I saw no need to support such monstrosities. I suspect the reason it is
in C at all is because of a bug in an early C compiler that people started
depending on <g>.

20/ Arrays and Strings
Arrays in LX are constructed generic types, not primitive types. So they
don't suffer of any of the problems you mention. I believe this solution is
more elegant. Also note that the LX syntax for declaring arrays (determined
by a written form on the generic type) doesn't suffer the issues you
mention on the C syntax, although there current left to right associativity
for named operators requires clumsy parentheses in some cases:
array A[1..5] of (pointer to integer)
As a reminder, the type is declared with some generic declaration like:
generic [range index; type item] type array
written array[index] of item
In declarations, only the first word appears on the left of the declared
name, thus a declaration of an array would use:
array A[1..5] of integer
Naturally, if you want zero-based arrays with a C-like notation, fine by
me:
generic [integer size; type item] type carray
written item[size]
integer X[5]
Note that this would cause trouble in the long-term, because the []
notation if not overloaded is for template arguments. Without the
declaration above, integer X[5] would be instantiating generic type integer
with argument 5. And since integer in LX is actually a generic type, this
should be valid...
There is no relationship between pointers and arrays in LX (except that
both are constructed types).
The same should hold for strings as for arrays. They should be constructed,
not primitive types. Did I already mention this? Built-in types are EVIL.
Christophe

21/ Synchronize
I am afraid that your approach to multitasking is too simplistic. In
addition, I believe that multitasking belongs to the library, not to the
language. LX will offer a multitasking library at some point, which I
started discussing from an interface point of view on my web page
somewhere. Most notably, with pragmas, you ensure that this seems
integrated in the language, yet it remains outside of it.
{synchronized} array Buffer[0..1023] of byte
The Ada community discovered the hard way that building a given tasking
model into the language was convenient, but ultimately closed too many
doors. Real-time multitasking doesn't require the same primitives as
web-server applications...
A possible use in LX of a tasking model implemented in a library can be
found at http://mozart-dev.sourceforge.net/lx_tasking.txt. It tries to
mimic the "Ada" feel of tasking, but that's just one possible model. The
Java feel is actually much simpler to implement :-)
PS: I promise, if I ever complete a tasking library, it will have
{synchronized} and {synchronised}, as well as {sincronised} for the
spelling-impaired :-)
Christophe

Why not just "synch" or "sync" ? ;) Long keywords == more typing.
Sean
"Christophe de Dinechin" <descubes earthlink.net> wrote in message
news:3B7C9867.C7F4A052 earthlink.net...

21/ Synchronize
I am afraid that your approach to multitasking is too simplistic. In
addition, I believe that multitasking belongs to the library, not to the
language. LX will offer a multitasking library at some point, which I
started discussing from an interface point of view on my web page
somewhere. Most notably, with pragmas, you ensure that this seems
integrated in the language, yet it remains outside of it.
{synchronized} array Buffer[0..1023] of byte
The Ada community discovered the hard way that building a given tasking
model into the language was convenient, but ultimately closed too many
doors. Real-time multitasking doesn't require the same primitives as
web-server applications...
A possible use in LX of a tasking model implemented in a library can be
found at http://mozart-dev.sourceforge.net/lx_tasking.txt. It tries to
mimic the "Ada" feel of tasking, but that's just one possible model. The
Java feel is actually much simpler to implement :-)
PS: I promise, if I ever complete a tasking library, it will have
{synchronized} and {synchronised}, as well as {sincronised} for the
spelling-impaired :-)
Christophe

22/ Try/Catch/Finally and destructors
I agree with the model. As a matter of fact, this reminds me that I need to
add finally as part of the try/catch statement in LX.
On the other hand, there are many cases where destructors are indeed
useful. For instance, locks, temporary files that need to be deleted when
you exit a procedure or delete an object, etc. And it's not like the
semantics or implementation is complicated either... Just avoid the X::~X
syntax, and you will be fine.
[LATER NOTE:] OK, you have destructors, and you do use the ~this syntax.
Yuck...
One important note: Bertrand Meyer, the author of Eiffel, makes a good
point about what he calls "disciplined exceptions." Which means, in short,
that by default, an exception repropagates rather than being "swallowed" by
a catch. That's the model for LX. You need to use 'resume' to prevent the
exception from propagating. The model is "catch clause as cleanup" rather
than "catch clause as implicit fix".
try
do_stuff
catch exception_A:
fixup_A
-- Implciitly rethrow here
catch exception_B:
fixup_B
retry
-- restart the 'try' block
catch exception_C
fixup_C
resume
-- C++ behavior here, quit the catch handler
Optimization note: "finally" is actually very bad for some optimizations on
"real, modern" CPUs (that is, not the x86 ;-) To Walter: that's largely
because it makes the control flow much more complex, everything now
connects to the finally, and the compiler must really take multiple exit
pathes into consideration.
Christophe

Christophe de Dinechin wrote in message <3B7C9971.8294C753 earthlink.net>...

To Walter: that's largely
because it makes the control flow much more complex, everything now
connects to the finally, and the compiler must really take multiple exit
pathes into consideration.

It's actually not that bad in practice. Remember that C++ has "finally"
clauses too, it's just that they are hidden (implicitly generated by the
compiler). The compiler builds finally clauses for each stack allocated
object that has a destructor.
I never much liked the hidden bloat generated by that. It's a major reason
why D has no stack allocated class objects, and "finally" clauses are
explicit.

Question:
If you have a class
class A
{
this() { ... }
~this() { ... }
}
Is a struct that contains one of these possible?
struct S
{
A a;
}
If so, that makes structs have implicit destructors.
Sean

It's actually not that bad in practice. Remember that C++ has "finally"
clauses too, it's just that they are hidden (implicitly generated by the
compiler). The compiler builds finally clauses for each stack allocated
object that has a destructor.
I never much liked the hidden bloat generated by that. It's a major reason
why D has no stack allocated class objects, and "finally" clauses are
explicit.

Something I'm really going to miss I think in D is guaranteed order of
destruction. I oftentimes use a class as a convenient way to automatically
run ctor and dtor code for initialization and cleanup. I have to use smart
pointer classes to get that, but it's worth it. In D order of destruction
cannot be guaranteed at all. Maybe I'll get used to it... calling explicit
Init and Exit functions instead of ctors/dtors etc. But it sure was nice to
have all that cleanup work done automatically by the compiler, especially
what with exceptions going off and everything, you don't have to put as much
work into each function exit point because you know the state of the system
will be consistent at each step.
An automated refcounting system could do that. I don't think GC every will
be able to reliably give me that. And I have many other resources to worry
about than just memory.
I'd hate for D to make this kind of thing worse instead of better.
Sean
"Walter" <walter digitalmars.com> wrote in message
news:9vf5q9$cq4$1 digitaldaemon.com...

Something I'm really going to miss I think in D is guaranteed order of
destruction. I oftentimes use a class as a convenient way to

automatically

run ctor and dtor code for initialization and cleanup. I have to use

smart

pointer classes to get that, but it's worth it. In D order of destruction
cannot be guaranteed at all. Maybe I'll get used to it... calling

explicit

Init and Exit functions instead of ctors/dtors etc. But it sure was nice

to

have all that cleanup work done automatically by the compiler, especially
what with exceptions going off and everything, you don't have to put as

much

work into each function exit point because you know the state of the

system

will be consistent at each step.

Don't forget about static constructors/destructors. They do exactly what
you want, and the latest spec (one with the alpha) defines strict order
of their execution:
"The order of static initialization is implicitly determined by the
import declarations in each module. Each module is assumed to depend on
any imported modules being statically constructed first. Other than
following that rule, there is no imposed order on executing the module
static constructors."
Since static constructors for modules you depend on are always called
first, it should be exactly what's needed to initialize a module.

Don't forget about static constructors/destructors. They do exactly what
you want, and the latest spec (one with the alpha) defines strict order
of their execution:
"The order of static initialization is implicitly determined by the
import declarations in each module. Each module is assumed to depend on
any imported modules being statically constructed first. Other than
following that rule, there is no imposed order on executing the module
static constructors."
Since static constructors for modules you depend on are always called
first, it should be exactly what's needed to initialize a module.

At first I had thought that this problem could not be solved, as I was too
stuck in a rut thinking about them the C++ #include way. Then I realized the
solution was staring me in the face <g> and the implementation almost wrote
itself.
One irritant about C++ (for me) was that in order to even have a module
constructor, I had to wrap it in a dummy class and then create a static
instance of that class. In D, I can just write what is intended - "run this
code on startup, and this code on termination".

One irritant about C++ (for me) was that in order to even have a module
constructor, I had to wrap it in a dummy class and then create a static
instance of that class. In D, I can just write what is intended - "run

this

code on startup, and this code on termination".

Now the question is, is it possible to run it without any class
at all? As far as I could understand from the docs, constructors -
even static - can only reside in classes. So your statement about
"wrapping it in a dummy class" still applies. Didn't you consider
enabling module-level constructors & finalizers - say, two _global_
functions called this() and ~this(), for consistency. Or maybe
initialization() & finalization(), as seen in Delphi. Whatever,
it would be just cool.
P.S. I want more bugs - give me a new alpha! =)

One irritant about C++ (for me) was that in order to even have a module
constructor, I had to wrap it in a dummy class and then create a static
instance of that class. In D, I can just write what is intended - "run

this

code on startup, and this code on termination".

Now the question is, is it possible to run it without any class
at all?

Then why "static" is needed, since there are no non-static
module constructors?

For consistency with the way they are defined for classes.

From my POV, module is sort of class, with only one instance
existing. As such, it might have a =non-static= constructor.
But in general, it's a matter of personal taste. So far,
why not just define "static" to be an option for module
constructors? And everybody would be satisfied...

From my POV, module is sort of class, with only one instance
existing. As such, it might have a =non-static= constructor.
But in general, it's a matter of personal taste. So far,
why not just define "static" to be an option for module
constructors? And everybody would be satisfied...

While that will certainly work, I'd like to stick with one way of doing
things at least in the initial release!

Because you don't want to publish the module ctor and dtor functions to be
called by other modules, perhaps.
Sean
"Pavel Minayev" <evilone omen.ru> wrote in message
news:9vi1fi$2032$1 digitaldaemon.com...

Then why "static" is needed, since there are no non-static
module constructors?

For consistency with the way they are defined for classes.

From my POV, module is sort of class, with only one instance
existing. As such, it might have a =non-static= constructor.
But in general, it's a matter of personal taste. So far,
why not just define "static" to be an option for module
constructors? And everybody would be satisfied...

That's a good reason. Wish I'd thought of it <g>.
"Sean L. Palmer" <spalmer iname.com> wrote in message
news:9vkevi$b24$1 digitaldaemon.com...

Because you don't want to publish the module ctor and dtor functions to be
called by other modules, perhaps.
Sean
"Pavel Minayev" <evilone omen.ru> wrote in message
news:9vi1fi$2032$1 digitaldaemon.com...

Then why "static" is needed, since there are no non-static
module constructors?

For consistency with the way they are defined for classes.

From my POV, module is sort of class, with only one instance
existing. As such, it might have a =non-static= constructor.
But in general, it's a matter of personal taste. So far,
why not just define "static" to be an option for module
constructors? And everybody would be satisfied...

Something I'm really going to miss I think in D is guaranteed order of
destruction. I oftentimes use a class as a convenient way to

automatically

run ctor and dtor code for initialization and cleanup. I have to use

smart

pointer classes to get that, but it's worth it. In D order of

destruction

cannot be guaranteed at all. Maybe I'll get used to it... calling

explicit

Init and Exit functions instead of ctors/dtors etc. But it sure was

nice

to

have all that cleanup work done automatically by the compiler,

especially

what with exceptions going off and everything, you don't have to put as

much

work into each function exit point because you know the state of the

system

will be consistent at each step.

Don't forget about static constructors/destructors. They do exactly what
you want, and the latest spec (one with the alpha) defines strict order
of their execution:
"The order of static initialization is implicitly determined by the
import declarations in each module. Each module is assumed to depend on
any imported modules being statically constructed first. Other than
following that rule, there is no imposed order on executing the module
static constructors."
Since static constructors for modules you depend on are always called
first, it should be exactly what's needed to initialize a module.

I forgot you can manually delete an object in D.
Still you don't really get the automatic dtor behavior... I suppose you
could have one object that must be dtor'd after another object, to hold a
pointer to said object.
Anyway, nothing that would prevent one from using the language.
Sean
"Pavel Minayev" <evilone omen.ru> wrote in message
news:9vkv3m$let$3 digitaldaemon.com...

That's neat and all, but I'm talking about individual class objects. Not
necessarily modules (though it's nice to see something akin to Turbo
Pascal's units again instead of this nightmarish #include bullshit!)
Sean

I know what you mean - you want to declare some functionality, and then have
some other functionality performed upon the closing }. I use just such a
thing for some simple timers, and some debug classes in C++. That feature
isn't in D, however.

There is a whole philosophy based on it. Check out 'Resource Management'

at
The reason was that C++ doesn't offer you any built-in capabilities
of things such as smart-pointers, and resource management in
general - which is probably what most demand. In D, however, you
can be sure that all trash will be occasionaly collected by the
GC. Of course, in cases where order is important, you have to
delete everything explicitly... but I personally have always
considered the way "always release after use" a good programming
practice...

Something I'm really going to miss I think in D is guaranteed order of
destruction. I oftentimes use a class as a convenient way to automatically
run ctor and dtor code for initialization and cleanup. I have to use smart
pointer classes to get that, but it's worth it.

You can use the synchronize statement to achieve the same affect, at least
for locking. Otherwise, you'll need to wrap the code in a try-finally
statement.
"Roland" <rv ronetech.com> wrote in message
news:3C1FB8E7.BAEE94DF ronetech.com...

"Sean L. Palmer" a écrit :

Something I'm really going to miss I think in D is guaranteed order of
destruction. I oftentimes use a class as a convenient way to

33/ Complex numbers
I believe that I can define in LX a complex type (and imaginary type) that
is as optimized as your implementation. But, since it is in the library, I
can also have: complex of ints, quaternions, tensors, etc. Why cover only
complex numbers? Mathematics did not stop a couple of centuries ago :-)
Well, someone else already made the point, methinks :-)
Oh, by the way, since in LX most things are in the library, this makes
several optimizations being actually resolved by operator overloading. For
instance, for a processor that has a multiply-and-add instruction (PowerPC,
IA-64)
function Add(integer A, B) return integer written A+B
{builtin add}
function MulAdd(integer A, B, C) return integer written A*B+C
{builtin mla}
You can refer to the CVS repository on SourceForge to see how the LX
compiler uses the {builtin} pragma to reference a table that defines the
opcodes to emit to the assembler...
Christophe

34/ HTML compilation
Ah, I see, you discarded the C preprocessor to replace it with an HTML
preprocessor. So #include is being replaced with <A REF="http://....">.
That's a clever trick.
Seriously, I think this is a cute idea, but it has nothing to do with D. It
is really a preprocessor that extracts source from HTML files, and that
ought to be a separate tool...
Christophe

Making it a separate tool means people won't use it. -Walter
The HTML thing is an experiment. I think some really cool editors can be
built using it.
Christophe de Dinechin wrote in message <3B7C9A6E.718B32AF earthlink.net>...

34/ HTML compilation
Ah, I see, you discarded the C preprocessor to replace it with an HTML
preprocessor. So #include is being replaced with <A REF="http://....">.
That's a clever trick.
Seriously, I think this is a cute idea, but it has nothing to do with D. It
is really a preprocessor that extracts source from HTML files, and that
ought to be a separate tool...
Christophe

What about the performance of printf vs some object oriented printing.
I would like
IO.WriteLn "A=", A, " and then B=", B, " and then C="
to be as fast as
printf( "A=%d and then B=%d and then C=%d", A, B, C );
In C++, it is no where near as fast and can never be as fast.
How do D and LX stack up when it comes to writing huge amounts
of formatted data?
Christophe de Dinechin wrote:

My last post tonight, because I'm tired of typing... The sieve in LX.
import IO = LX.Text_IO
procedure main() is
with integer i, count, prime, k, iter
with bits flags[8191]
for iter in 1..10 loop
count := 0
flags[0..8191] := true
for i in flags.index loop
if flags[i] then
prime := i+i+3
k := i + prime
for k in i+prime..flags.index.high step prime loop
flags[k] := false
count += 1
IO.WriteLn count, " primes"
And just because people will find bugs in my posts, let me mention it:
there is a bug in the D implementation of the Sieve: it should be bit[8191]
flags, methinks. Unless, of course, bit is special in that respect. It
could be, it's a built-in type. And did I mention that built-in types are
EVIL?
Christophe

Ach, a post by Chris Friesen reminded me about the switch statement. LX has
two forms, one of them I care about, the other being just syntactic sugar.
Syntactic sugar first:
case
when X > 0: do_something_with X
when Y > 0: do_something_with Y
when others: barf
This is really another way to write "elseif". I initially thought I needed
that construct because of the indent-sensitive syntax in LX, but it turns
out that the indentation-checking is flexible enough to accomodate else
if... I still kept the construct...
The important one:
procedure CheckInt(integer N) is
case N is
when 1: WriteLn "N is small"
when 2..5: WriteLn "I can count N on one hand"
when 6..10: WriteLn "I need to use two hands"
when other: WriteLn "Sorry, can't do"
What is interesting is that it uses a helper, variable-argument-size
function to determine which case to take. So you can have a case statement
with most anything. The helper function is called Index(X, T1, T2, ...,
TN), and it returns the 1-based index of X inside the T1..TN objects. If
none matches, it must return an out of range value (N+1 or 0 being good
candidates)
So for instance you can write an Index function that makes the following
legal:
procedure Hit(point P; rectangle R; circle C) is
case P is
when R: WriteLn "We hit the rectangle"
when C: WriteLn "We hit the circle"
when other: WriteLn "That's a miss...
Christophe

Ach, yet another post from the silly LX guy. Someone else talked about
delegation. Delegation is probably the one thing that makes nEXtstEp
(sorry, MacOS X 10.0.4.1 beta2 preview 3) implementable in Objective-C but
not easily in C++. You ought to have this in D.
A practical example: I have a window object, that can respond to several
"messages" (display, resize, etc). On this window, I have several objects
that know about some of the messages, but others they just don't care. One
object which has the focus is the one that receives the messages from the
operating system.
The key issue here is: how do I forward messages that I don't care about to
the window behind me, or to some other object that cares? Say the front
widget interfcepts "Draw" messages, but just doesn't want to know anything
about all others, such as "Resize", "Shutdown", etc...
Let's try it the C++ way:
class Window {
virtual void Draw(...);
virtual void Resize(...);
... // 12895 other members
}; // Don't forget the semi-colon, it's really important, you know...
OK, now, time to implement "widget". The key thing is that, from the
operating system's point of view, Widget has to respond to the Window
messages. So in C++, it derives from Window, right? Wrong! Let's try that
first to see the problem. Assume for now that you try that for a Widget
that cares only about the "Draw" message.
class Widget : public Window {
Window *behind_me;
virtual void Draw(...) { /* something really great here */ }
virtual void Resize(...) { behind_me->Resize(...); }
// 12894 other members like Resize...
};
The sad thing is that there is code out there actually written that way.
Two major problems: it's very cumbersome to write, and the Widget inherits
all of the Windows baggage, which it really doesn't care much about. And
what if in MacOS X 10.0.4.1 beta2 preview 4, the OS vendor decides to add a
member Recolor(...) in Window? Well, you are out of luck, because you do a
very wrong thing with that: you just pass it to the base class, which
thinks that it is the main window, and reformats your hard disk as a
result.
Other approaches: the Window base class may itself have a "delegate" field,
and delegate everything. Not much better. There are a few other solutions.
But ultimately, the only solutions that I saw working in C++ ran along the
lines of having a single "SendMessage()" virtual function in the base
class, and then a big switch statement that invokes other member functions.
Which is just a way of implementing a much less efficient version of the
Objective-C runtime system...
In Objective-C, all message passing occurs in such a way that you can
forward unknown messages to someone else, hoping that the receiving object
knows what to do with it. Forwarding includes all method arguments, etc.
In LX, a limited form of delegation is made possible by user-defined
implicit conversions. I'm not completely satisfied with the solution yet.
You would have:
type window
function Draw(window W, ...)
function Resize(window W, ...)
type widget with
window Delegate
function window(widget W) return window written W is
return W.Delegate
precedure Draw(widget W)
-- Now you can write:
procedure Test(widget W)
Draw W -- Draw the widget
Resize W -- Resize the window (delegate)
As I said, I am not totally satisfied. Compared to Objective-C, it restores
type safety (you give it up with the implicit conversion for only one
type). On the other hand, it still has usability problems with dynamic
dispatch and a few other things... Anyway, this is not about LX, it's about
D, so if someone comes up with a cool solution in D... I'll be happy to
borrow it for LX :-)
Christophe

What about runtime analysis of a classes members. Instead of assuming and
depending on a classes function members to be located in one place, have it
do a search and find the location of the function at runtime? Or if you
insist upon using messages, why not use object identifiers with function
name calls. So I can call a function of a class through messages, if the
function does not exist it returns an error, if the function has moved it
will find it and call it properly. Hmmm, maybe messages are better after
all, because I could shut off an object to not recieve messages, alter that
object and then turn it back on to recieve messages, this by
manipulating/reprogramming an object at runtime (maybe I am being a runtime
freak :)).
Messages would be really interesting come to think of it. An object would
need to have a socket where by a delegate could be placed in there, that
delegate would know what message to send and whom to send it to. An object
that sends messages would need these sockets for delegation. There really is
no event handlers anymore, any function could be an event handler, its now
on the other side, the delegates reside in the event generator and control
what function is called in response to the generation of that event by
sending a message to that function.
It may even be possible to mix messages and direct function calls, delegate
objects/types could be used to mask what is really happening underneath.

What you are describing is more or less the Objective-C runtime.
In Obj-C, when you write:
[object drawAtX: x Y: y]
what it does is create a message packet with a "selector" of drawAtX:y: and
arguments (object, x, y), and then invoke a runtime routine (something like
__objc_msgSend) to lookup runtime tables for the object that knows how to
respond to that. It first checks the given target (object). But the given
target may not respond to the message, in which case various forwarding
mechanisms exist.
Christophe
interested wrote:

What about runtime analysis of a classes members. Instead of assuming and
depending on a classes function members to be located in one place, have it
do a search and find the location of the function at runtime? Or if you
insist upon using messages, why not use object identifiers with function
name calls. So I can call a function of a class through messages, if the
function does not exist it returns an error, if the function has moved it
will find it and call it properly. Hmmm, maybe messages are better after
all, because I could shut off an object to not recieve messages, alter that
object and then turn it back on to recieve messages, this by
manipulating/reprogramming an object at runtime (maybe I am being a runtime
freak :)).
Messages would be really interesting come to think of it. An object would
need to have a socket where by a delegate could be placed in there, that
delegate would know what message to send and whom to send it to. An object
that sends messages would need these sockets for delegation. There really is
no event handlers anymore, any function could be an event handler, its now
on the other side, the delegates reside in the event generator and control
what function is called in response to the generation of that event by
sending a message to that function.
It may even be possible to mix messages and direct function calls, delegate
objects/types could be used to mask what is really happening underneath.

A practical example: I have a window object, that can respond to several
"messages" (display, resize, etc). On this window, I have several objects
that know about some of the messages, but others they just don't care. One
object which has the focus is the one that receives the messages from the
operating system.

I agree, looking at all the application frameworks out there, MFC, MacApp
and so forth, they all have a common theme, pass around messages or events
from one point to another. As C and C++ does not have runtime binding easily
available, what happens is that the framework designers write very
convoluted macro or lookup systems for making sure a message is passed from
one point to another. Objective-C makes this far easier with its message
passing construct. If D has this option, and it's of course another
performance burden, but not as bad as one thinks, it would open up a lot of
good designs concerning writing application frameworks. If one even thinks
big and possibly uses a message passing system that could make use of open
standards for calling other entities (SOAP, XML-RPC, and so forth), it could
also provide a lot of good solutions. --Kent

How does LX compare when it comes to runtime (
see also the "What is the D runtime like?" thread).
Specifically (reposting my last post to the above thread):
What does that mean with regards to:
running programs that contain LX modules from multiple LX compiler versions
features especially if the object model changes between the two?
what about stuff like GC?
running programs that have a C mainline and are linked with the C linker.
running programs that have a C++ mainline?
running a D module in a kernel?
running a D module as a COM inproc component?
Christophe de Dinechin wrote:

Interestingly, my background is close to that of Walter: I also
developed video games for a living, I also worked on a commercial C++
implementation, and I also designed my own language, called LX. More
information on LX can be found at http://mozart-dev.sf.net.
So I am going to start a big series of post to try to compare D and LX.
To avoid polluting the newsgroup too much, I am going to try to post all
of them under the same thread.
I am obviously biased. On the other hand, I believe there is much to be
gained if we exchange ideas and compare designs. Please bear with me...
Christophe

Oops, I started changing references from D to LX below and didn't get
all the way through. All of the questions pertain to LX, not to D.
Phil Brooks wrote:

How does LX compare when it comes to runtime (
see also the "What is the D runtime like?" thread).
Specifically (reposting my last post to the above thread):
What does that mean with regards to:
running programs that contain LX modules from multiple LX compiler versions
features especially if the object model changes between the two?
what about stuff like GC?
running programs that have a C mainline and are linked with the C linker.
running programs that have a C++ mainline?
running a D module in a kernel?
running a D module as a COM inproc component?
Christophe de Dinechin wrote:

Interestingly, my background is close to that of Walter: I also
developed video games for a living, I also worked on a commercial C++
implementation, and I also designed my own language, called LX. More
information on LX can be found at http://mozart-dev.sf.net.
So I am going to start a big series of post to try to compare D and LX.
To avoid polluting the newsgroup too much, I am going to try to post all
of them under the same thread.
I am obviously biased. On the other hand, I believe there is much to be
gained if we exchange ideas and compare designs. Please bear with me...
Christophe