www.digitalmars.com         C & C++   DMDScript  

D - D vs. LX

reply Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
next sibling parent reply Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
next sibling parent "jacob navia" <jacob jacob.remcomp.fr> writes:
"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.
Aug 17 2001
prev sibling next sibling parent "Walter" <walter digitalmars.com> writes:
Christophe de Dinechin wrote in message <3B7C8D0B.DC4770AD earthlink.net>...
 I hope you covered the various cases of
machine idioms that I documented in
http://home.earthlink.net/~descubes/C--

Thanks for the laugh! Great satire.
Aug 17 2001
prev sibling parent Axel Kittenberger <axel dtone.org> writes:
Christophe de Dinechin wrote:

 
 1/ Macros
 

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.
Aug 17 2001
prev sibling next sibling parent Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
prev sibling next sibling parent Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
prev sibling next sibling parent Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
prev sibling next sibling parent reply Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
parent reply "Walter" <walter digitalmars.com> writes:
Christophe de Dinechin wrote in message <3B7C8E79.109F4270 earthlink.net>...
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.

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.
Aug 23 2001
parent reply Charles Hixson <charleshixsn earthlink.net> writes:
Walter wrote:
 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.
 

large as the equivalent STL for C++. So the dealing with it may not have been easy.
Aug 23 2001
parent reply "Walter" <walter digitalmars.com> writes:
"Charles Hixson" <charleshixsn earthlink.net> wrote in message
news:3B851445.6010005 earthlink.net...
 Walter wrote:
 Christophe de Dinechin wrote in message


...

 I like the explicit instantiation. Since there is an STL workalike for


 which I haven't looked at, somebody must have dealt with this problem.

large as the equivalent STL for C++. So the dealing with it may not have been easy.

Large in source or large in object?
Aug 23 2001
parent reply Russell Bornschlegel <kaleja estarcion.com> writes:
Walter wrote:
 
 "Charles Hixson" <charleshixsn earthlink.net> wrote in message
 news:3B851445.6010005 earthlink.net...
 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
Aug 23 2001
parent reply Charles Hixson <charleshixsn earthlink.net> writes:
Russell Bornschlegel wrote:
 
 Walter wrote:
 
"Charles Hixson" <charleshixsn earthlink.net> wrote in message
news:3B851445.6010005 earthlink.net...

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.


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.
Aug 23 2001
parent reply "Walter" <walter digitalmars.com> writes:
Charles Hixson wrote in message <3B856897.6050907 earthlink.net>...
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.
Aug 25 2001
parent Charles Hixson <charleshixsn earthlink.net> writes:
Walter wrote:
 Charles Hixson wrote in message <3B856897.6050907 earthlink.net>...
 
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.

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).
Aug 27 2001
prev sibling next sibling parent Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
prev sibling next sibling parent Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
prev sibling next sibling parent reply Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
parent reply "jacob navia" <jacob jacob.remcomp.fr> writes:
"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

 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?
Aug 17 2001
parent Christophe de Dinechin <descubes earthlink.net> writes:
Jacob,


It is implemented, and it "works" (that is, I did not find the bugs yet).
Explicit parentheses are eliminated during parsing, the tree form is the same,
so (A*B)+C is also MultiplyAndAdd. The tree structure being considered is
different for A+(B*C), so there is no match.


[localhost:~/Development/mozart/lx] ddd% cat test.lx
type matrix

function mla(matrix A, B, C) written A*B+C

matrix P, Q, R
matrix M := P * Q + R
matrix N := (P*Q) + R
matrix O := P* (Q+R)


[localhost:~/Development/mozart/lx] ddd% ./lx -parse test.lx -run_semantics
-stdout
test.lx:9: No written form matches 'P * (Q + R) '.
-- Generated by LX using lx-text.stylesheet
import  LX_BUILTINS
using LX_BUILTINS

variable  type matrix
constant  procedure mla( in matrix A;  in matrix B;  in matrix C)
   written A_ * B_ + C_
variable  matrix P
variable  matrix Q
variable  matrix R
variable  matrix M := mla (P, Q, R)
variable  matrix N := mla (P, Q, R)
variable  matrix O := P * (Q + R)
-- Thank you for using LX.
[localhost:~/Development/mozart/lx] ddd%


Christophe


jacob navia wrote:

 "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

 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?

Aug 18 2001
prev sibling next sibling parent reply Christophe de Dinechin <descubes earthlink.net> writes:
 9/ Object oriented gradualism

I just have no idea what you mean by that :-)


Christophe
Aug 16 2001
parent "Walter" <walter digitalmars.com> writes:
For example, you only get a virtual function pointer in a class if the class
actually has any virtual functions in it.

Christophe de Dinechin wrote in message <3B7C90A8.DF87D385 earthlink.net>...
 9/ Object oriented gradualism

I just have no idea what you mean by that :-)


Christophe

Aug 23 2001
prev sibling next sibling parent Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
prev sibling next sibling parent Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
prev sibling next sibling parent reply Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
parent "Walter" <walter digitalmars.com> writes:
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.
Aug 23 2001
prev sibling next sibling parent Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
prev sibling next sibling parent reply Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
parent reply "Sean L. Palmer" <spalmer iname.com> writes:
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

Oct 22 2001
parent reply "Walter" <walter digitalmars.com> writes:
Sean L. Palmer wrote in message <9r1rj3$2etr$1 digitaldaemon.com>...
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.

Forward references would look just like in C: int foo();
Oct 23 2001
parent reply Axel Kittenberger <axel dtone.org> writes:
 Forward references would look just like in C:
 
     int foo();

I think strictly ansi speaking this is not a valid prototype, strict C wants to have it look like this: int foo(void);
Oct 23 2001
parent "Sean L. Palmer" <spalmer iname.com> writes:
This post now thoroughly cleansed of nits.  ;)

Sean

"Axel Kittenberger" <axel dtone.org> wrote in message
news:9r35rp$5ls$2 digitaldaemon.com...
 Forward references would look just like in C:

     int foo();

I think strictly ansi speaking this is not a valid prototype, strict C wants to have it look like this: int foo(void);

Oct 23 2001
prev sibling next sibling parent Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
prev sibling next sibling parent Christophe de Dinechin <descubes earthlink.net> writes:
16/ Modules - Encapsulation

There are two orthogonal aspects that are commonly called "modules": 1/
encapsulating related stuff, and 2/ making declarations available across
translation units.

For the first aspect, LX uses the "module" type, which is defined as:

    type module is constant record

You define a module interface as follows:

    module COMPLEX with
        type complex
        constant complex I
        function complex (real Re := 0.0, Im := 0.0) return complex
        function Add(complex A, B) return complex written A+B
        function Sub(complex A, B) return complex written A-B
        ...

You define a module implementation as follows (notice how 'body' comes
handy here to avoid repeating the whole interface):

    module COMPLEX body is
        type complex is record with
            real Re, Im
        constant complex I is complex(0.0, 1.0)
        function complex body is
            result.Re := Re
            result.Im := Im
        function Add body is
            result.Re := A.Re + B.Re
            result.Im := A.Im + B.Im
        ...


Because a module is just a constant record, there are many things that are
vastly simplified, notably lookup rules. Also, a 'using' statement works
both for modules and for objects, since they are essentially the same
thing.

    using COMPLEX
    complex Z
    using Z
    Re := 0.0
    Im := 1.0


Christophe
Aug 16 2001
prev sibling next sibling parent Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
prev sibling next sibling parent Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
prev sibling next sibling parent Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
prev sibling next sibling parent reply Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
parent "Walter" <walter digitalmars.com> writes:
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>.
Aug 22 2001
prev sibling next sibling parent Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
prev sibling next sibling parent reply Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
parent "Sean L. Palmer" <spalmer iname.com> writes:
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

Oct 22 2001
prev sibling next sibling parent reply Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
parent reply "Walter" <walter digitalmars.com> writes:
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.
Sep 03 2001
parent reply "Sean L. Palmer" <spalmer iname.com> writes:
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.

Oct 22 2001
parent reply "Walter" <walter digitalmars.com> writes:
"Sean L. Palmer" <spalmer iname.com> wrote in message
news:9r1s8a$2fbv$1 digitaldaemon.com...
 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.

What happens is that the "A a;" creates a reference to an A, not an instance of A. So, no implicit destructors are required.
Dec 15 2001
parent reply "Sean L. Palmer" <spalmer iname.com> writes:
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...
 "Sean L. Palmer" <spalmer iname.com> wrote in message
 news:9r1s8a$2fbv$1 digitaldaemon.com...
 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.

What happens is that the "A a;" creates a reference to an A, not an

 of A. So, no implicit destructors are required.

Dec 15 2001
next sibling parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Sean L. Palmer" <spalmer iname.com> wrote in message
news:9vfc86$g7k$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

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

 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

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

 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

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

 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.
Dec 15 2001
next sibling parent reply "Walter" <walter digitalmars.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:9vfhhd$j5q$1 digitaldaemon.com...
 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".
Dec 15 2001
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Walter" <walter digitalmars.com> wrote in message
news:9vg29o$s9o$2 digitaldaemon.com...

 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

 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! =)
Dec 15 2001
parent reply "Walter" <walter digitalmars.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:9vg3rr$sue$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> wrote in message
 news:9vg29o$s9o$2 digitaldaemon.com...
 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

 code on startup, and this code on termination".

at all?

Yes! Just call them "static this() { }" and "static ~this() { }" at the module level.
 P.S. I want more bugs - give me a new alpha! =)

I'm working on it. I took some time off to put out 8.25 of the C++ compiler, basically an accumulation of various fixes.
Dec 15 2001
parent reply "Pavel Minayev" <evilone omen.ru> writes:
----- Original Message -----
From: "Walter" <walter digitalmars.com>
Newsgroups: D
Sent: Saturday, December 15, 2001 11:35 PM
Subject: Re: D vs. LX - Exceptions


 Yes! Just call them "static  this() { }" and "static ~this() { }" at the
 module level.

And what if I omit "static"?
Dec 15 2001
parent reply "Walter" <walter digitalmars.com> writes:
You should get an error message.

"Pavel Minayev" <evilone omen.ru> wrote in message
news:9vgd4n$12as$1 digitaldaemon.com...
 ----- Original Message -----
 From: "Walter" <walter digitalmars.com>
 Newsgroups: D
 Sent: Saturday, December 15, 2001 11:35 PM
 Subject: Re: D vs. LX - Exceptions


 Yes! Just call them "static  this() { }" and "static ~this() { }" at the
 module level.

And what if I omit "static"?

Dec 15 2001
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Walter" <walter digitalmars.com> wrote in message
news:9vglbk$16vc$1 digitaldaemon.com...

 You should get an error message.

Then why "static" is needed, since there are no non-static module constructors?
Dec 16 2001
parent reply "Walter" <walter digitalmars.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:9vhkt5$1pfv$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> wrote in message
 news:9vglbk$16vc$1 digitaldaemon.com...

 You should get an error message.

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

For consistency with the way they are defined for classes.
Dec 16 2001
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Walter" <walter digitalmars.com> wrote in message
news:9vhs0a$1t8o$1 digitaldaemon.com...
 "Pavel Minayev" <evilone omen.ru> wrote in message
 news:9vhkt5$1pfv$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> wrote in message
 news:9vglbk$16vc$1 digitaldaemon.com...

 You should get an error message.

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...
Dec 16 2001
next sibling parent "Walter" <walter digitalmars.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message news:9vi1fi$2032
 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!
Dec 16 2001
prev sibling parent reply "Sean L. Palmer" <spalmer iname.com> writes:
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...
 "Walter" <walter digitalmars.com> wrote in message
 news:9vhs0a$1t8o$1 digitaldaemon.com...
 "Pavel Minayev" <evilone omen.ru> wrote in message
 news:9vhkt5$1pfv$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> wrote in message
 news:9vglbk$16vc$1 digitaldaemon.com...

 You should get an error message.

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...

Dec 17 2001
parent "Walter" <walter digitalmars.com> writes:
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...
 "Walter" <walter digitalmars.com> wrote in message
 news:9vhs0a$1t8o$1 digitaldaemon.com...
 "Pavel Minayev" <evilone omen.ru> wrote in message
 news:9vhkt5$1pfv$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> wrote in message
 news:9vglbk$16vc$1 digitaldaemon.com...

 You should get an error message.

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...


Dec 17 2001
prev sibling parent reply "Sean L. Palmer" <spalmer iname.com> writes:
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

"Pavel Minayev" <evilone omen.ru> wrote in message
news:9vfhhd$j5q$1 digitaldaemon.com...
 "Sean L. Palmer" <spalmer iname.com> wrote in message
 news:9vfc86$g7k$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

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

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


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

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


 to
 have all that cleanup work done automatically by the compiler,


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

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

 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.

Dec 17 2001
next sibling parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Sean L. Palmer" <spalmer iname.com> wrote in message
news:9vkf3j$bb7$1 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!)

What's wrong with objects? Operator new calls the constructor, garbage collector will then remove the object, calling destructor for you, or you can explicitly request to remove the object you want.
Dec 17 2001
parent "Sean L. Palmer" <spalmer iname.com> writes:
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...
 "Sean L. Palmer" <spalmer iname.com> wrote in message
 news:9vkf3j$bb7$1 digitaldaemon.com...

 That's neat and all, but I'm talking about individual class objects.


 necessarily modules (though it's nice to see something akin to Turbo
 Pascal's units again instead of this nightmarish #include bullshit!)

What's wrong with objects? Operator new calls the constructor, garbage collector will then remove the object, calling destructor for you, or you can explicitly request to remove the object you want.

Dec 18 2001
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"Sean L. Palmer" <spalmer iname.com> wrote in message
news:9vkf3j$bb7$1 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.
Dec 17 2001
parent reply "Rajiv Bhagwat" <dataflow vsnl.com> writes:
There is a whole philosophy based on it. Check out 'Resource Management' at
www.relisoft.com
- Rajiv

"Walter" <walter digitalmars.com> wrote in message
news:9vlrcd$16lh$2 digitaldaemon.com...
 "Sean L. Palmer" <spalmer iname.com> wrote in message
 news:9vkf3j$bb7$1 digitaldaemon.com...
 That's neat and all, but I'm talking about individual class objects.


 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

 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.

Dec 17 2001
parent "Pavel Minayev" <evilone omen.ru> writes:
"Rajiv Bhagwat" <dataflow vsnl.com> wrote in message
news:9vml4d$1nlb$1 digitaldaemon.com...

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

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...
Dec 17 2001
prev sibling parent reply Roland <rv ronetech.com> writes:
"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 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.

I've never realised that (ready D specs, it's time i try it..) For example in C++: class LockDevice { public: LockDevice() { ++_busy; //atomic } ~LockDevice() { _busy--; //atomic } int isbusy() { return (_busy!=0); } int busylevel() { return _busy; } private: static int _busy; }; int LockDevice::_busy = 0; void workondevice() { LockDevice lock; //lock device acces if (lock.busylevel()>1) then goto device_is_already_locked; . <work on device> . } //~LockDevice automaticaly unlock device Can i do something similar in D ? Roland
Dec 18 2001
parent reply "Walter" <walter digitalmars.com> writes:
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


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


 pointer classes to get that, but it's worth it.

I've never realised that (ready D specs, it's time i try it..) For example in C++: class LockDevice { public: LockDevice() { ++_busy; file://atomic } ~LockDevice() { _busy--; file://atomic } int isbusy() { return (_busy!=0); } int busylevel() { return _busy; } private: static int _busy; }; int LockDevice::_busy = 0; void workondevice() { LockDevice lock; file://lock device acces if (lock.busylevel()>1) then goto device_is_already_locked; . <work on device> . } file://~LockDevice automaticaly unlock device Can i do something similar in D ? Roland

Dec 18 2001
parent reply Roland <rv ronetech.com> writes:
Walter a écrit :

 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.

nice any plan to port D on DOSX ? does it seem complicate ? Roland (sorrry if this question had already been asked, this group is too fast and flight too high for me)
Dec 19 2001
parent reply "Walter" <walter digitalmars.com> writes:
"Roland" <rv ronetech.com> wrote in message
news:3C20B16D.5389BC0D ronetech.com...
 Walter a écrit :

 You can use the synchronize statement to achieve the same affect, at


 for locking. Otherwise, you'll need to wrap the code in a try-finally
 statement.

nice any plan to port D on DOSX ? does it seem complicate ? Roland (sorrry if this question had already been asked, this group is too fast

 flight too high for me)

The only barrier to running D under DOSX is porting the runtime library.
Dec 19 2001
parent reply Roland <rv ronetech.com> writes:
Walter a écrit :

 any plan to port D on DOSX ? does it seem complicate ?

 Roland


The good news is that it doen't seem too complicate The bad news is that i can't propose my help at this moment and may be Linux will come first ? Thanks Roland
Dec 20 2001
parent reply "Walter" <walter digitalmars.com> writes:
"Roland" <rv ronetech.com> wrote in message
news:3C21E0E6.E9A2C732 ronetech.com...
 Walter a écrit :
 any plan to port D on DOSX ? does it seem complicate ?
 Roland


The bad news is that i can't propose my help at this moment and may be

 will come first ?
 Thanks
 Roland

I won't myself be doing the linux port, I have a partner in crime who's looking into it. Porting the phobos rtl shouldn't be too hard. Don't ask for a 16 bit port <g>.
Dec 20 2001
parent "Pavel Minayev" <evilone omen.ru> writes:
"Walter" <walter digitalmars.com> wrote in message
news:9vt62f$1em4$3 digitaldaemon.com...

 I won't myself be doing the linux port, I have a partner in crime who's
 looking into it. Porting the phobos rtl shouldn't be too hard. Don't ask

 a 16 bit port <g>.

Oh my god, please, no, I want to make .com files with D! =)
Dec 20 2001
prev sibling next sibling parent Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
prev sibling next sibling parent reply Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
parent "Walter" <walter digitalmars.com> writes:
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

Sep 02 2001
prev sibling next sibling parent reply Christophe de Dinechin <descubes earthlink.net> writes:
35/ printf rules?

printf sucks. Really. I could write 218918231231 pages on this.

    -- Unformatted IO
    IO.WriteLn "A=", A, " and then B=", B

    -- Formatted IO
    IO.WriteLn A format "A=####", B format "B="#5.9#"

    -- Definition of WriteLn
    generic type writable if
        with writable W
        IO.Write W

    procedure WriteLn() is
        Write Unicode.CR

    procedure WriteLn(writable X; others) is
        Write X
        WriteLn others


Now, if you really care about the printf formatting idiotisms, you can
implement that in LX too. Not that I would recommend it...


Christophe
Aug 16 2001
parent reply Phil Brooks <phil_brooks mentor.com> writes:
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:
 
 35/ printf rules?
 
 printf sucks. Really. I could write 218918231231 pages on this.
 
     -- Unformatted IO
     IO.WriteLn "A=", A, " and then B=", B
 
     -- Formatted IO
     IO.WriteLn A format "A=####", B format "B="#5.9#"
 
     -- Definition of WriteLn
     generic type writable if
         with writable W
         IO.Write W
 
     procedure WriteLn() is
         Write Unicode.CR
 
     procedure WriteLn(writable X; others) is
         Write X
         WriteLn others
 
 Now, if you really care about the printf formatting idiotisms, you can
 implement that in LX too. Not that I would recommend it...
 
 Christophe

Sep 13 2001
parent "Walter" <walter digitalmars.com> writes:
Phil Brooks wrote in message <3BA147BC.DA11B7AB mentor.com>...
How do D and LX stack up when it comes to writing huge amounts
of formatted data?

D will support use of printf(), other methods may be supplied via the runtime library.
Sep 15 2001
prev sibling next sibling parent Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
prev sibling next sibling parent Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
prev sibling next sibling parent reply Christophe de Dinechin <descubes earthlink.net> writes:
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
Aug 16 2001
next sibling parent reply "interested" <here nowhere.net> writes:
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.
Aug 17 2001
parent Christophe de Dinechin <descubes earthlink.net> writes:
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.

Aug 17 2001
prev sibling parent "Kent Sandvik" <sandvik excitehome.net> writes:
 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
Aug 17 2001
prev sibling parent reply Phil Brooks <phil_brooks mentor.com> writes:
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

Sep 13 2001
parent Phil Brooks <phil_brooks mentor.com> writes:
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


Sep 13 2001