Re: The Language I want
- From: "Dmitry A. Kazakov" <mailbox@xxxxxxxxxxxxxxxxx>
- Date: Mon, 9 Mar 2009 10:52:25 +0100
On Sun, 8 Mar 2009 19:32:19 -0000, Aaron Gray wrote:
"Dmitry A. Kazakov" <mailbox@xxxxxxxxxxxxxxxxx> wrote in message
news:z9uxovo1m6pj.1urlzkw2mvy1c.dlg@xxxxxxxxxxxxx
No. Visibility is an independent to type issue. It is a property of a
package / module. It has nothing to do with inheritance. So public and
private views only.
no protected members ? Why not ?
Because it is an artefact of C++ design fault which conflates classes and
modules. If you don't then public and private suffice.
I disagree, protected allows you to call or modify methods of your parent
but not to allow using classes to access those protected members. This is
very useful for controlled encapsulation.
If you still disagree could you make your point clearer as I not sure I
understand what you are getting at ?
The point is that visibility belongs to package / module. It is not the
business of a type declaration. The package may declare several types
related in their implementation. So the package body would provide
visibility to the implementation of the type A to one the type B, provide
completely hidden operations and data shared by them etc.
This all is extremely painful in the model you describe:
1. You need awful "friend class"
2. "friend" still does not work, when you add new classes
3. You need static methods. With packages you just put things you need into
the package.
4. You need singletons when the module requires elaboration.
5. This still does not work if you need to elaborate several types, their
data etc. C++ has massive problems with the elaboration order. With
packages you have a clear control upon what depends on what, and in which
order static things come into life.
6. Same applies to finalization.
No. Constructor / destructor aren't functions. There should be built-in
constructors / destructors with user defined hooks of *properly* typed
subprograms.
They are static functions with special prominence.
No, they are not since you cannot specify their profile staying typed.
Constructor takes "no type" and converts it into "type T," this i
fundamentally inconsistent with the notion of strong typing.
BTW, under static function I understand "compile time function," and not
C++ kludge.
No I still define 'static' as C++'s static in my model.
These "class constructors and distructors" are one off functions called when
a class is "loaded" or "unloaded" ie when it comes into being or dies, this
allows static data members to be dynamically initialized.
They are not functions because, as I said, you cannot provide a signature
for. Consider a type T. Now what is the signature of a default constructor
of T, if it were a function or procedure? What would happen if somebody
calls this thing? Once, twice etc. You have to put a lot of words around
this "function," explaining why it behaves not in a way one would expect
from a normal function. If something does not behave as a function, then it
is not one.
- most things/'units' can exhibit inheritance, for example
modules can inherit other modules so extending them.
First-class modules? That's difficult to make working. I doubt if it can
work.
Dont see any problems with first class modules, can you please list some.
How are you going to check types of the objects declared in dynamically
created module object?
Because everything is static, when a textual module is first loaded it is
compiled into a dll then loaded.
That is for sure, but what is static for the clients of the module? If you
treat a module as a first-class thing, then I would expect to be able to do
things like a function that returns a module. Is it allowed? Now consider
some context where I call this function, obtain a module and then call a
function from that module. Where I get the types of the function
parameters? They come from the module. How can I statically check them?
I suppose you mean a much weaker thing than "truly" first-class modules.
What you mean is rather a sort of late binding.
It is not that I am against it. For distributed systems first-class modules
are of paramount importance. I just do not know how to reconcile things
without falling into self-interpreting programs and other "dynamic" havoc.
- generics/parameterized polymorphism
No generics. Constrained types instead.
Why ?
Generics is a huge mess.
Real generics, ie not C++'s generics are great they allow very efficient
good code to be generated particularly when combined with inline
functions/methods, saving lots of (physical) typing effort.
They are:
1. Unmaintainable
2. Unreadable
3. Non-testable (you can test only instances, not the generics themselves)
4. Hugely inefficient (templates cannot be shared)
5. Impossible to build anything bigger than boy examples. Consider 100K
source lines of generic code + one compiler error message. The objective:
understand the message, fix the problem
6. No compiler can implement more or less elaborated generic framework.
This problem is known for C++ and for Ada compilers. I saw no one which
would not have severe bugs in the implementation of generics. It just does
not work, they fix one bug and introduce another
7. As for typing efforts, generics lead to a geometric explosion of the
source code. Yes, I mean the source code, not the expanded one. This
happens because generic language is very low level and lacks all important
abstractions. C++ templates are untyped, have no subprograms etc. Add here
that this is static, so "once generic, always generics." In the result the
programmer is forced to program in a language which abstraction level is
about of FORTRAN-IV or lower. No wonder that he would cut and paste
generics as hell.
8. Reuse is minimal
9. Refactoring is almost impossible
[I have an extensive experience with using generics in large projects. That
is the reason why I hate them so much and nobody can convince me in
opposite. We have to agree to disagree.]
They are sort of
oposite to polymorphism in a way.
They are polymorphism, called "parametric" and sometimes "static."
Templates are not C++ templates. With C++ the templates object signature ie
its mangled name in assembler contains all the templates instantiated
parameter types. My generics would too. But my templates do not.
So you claim it is not a mess? (:-)
Also the compiler should be able to decide how to implement things, virtual
or non virtual, or inlined, given the context such as generic parameters if
there are any, and small world syndrome / whole program optimization should
allow the right decisions to be made.
I would like a nonvirtual keyword then :)
You don't need any word because the type is different. When classes and
types are distinct, the type of the class is not the type of the class'
root type. So
f : T (specific for T, dispatches on the class T)
f : class T (specific for class T, i.e. it does not dispatch on it)
You probably mean the interface of a compilation unit and its separation
from the implementation. Everything visible in the declarative part of a
unit is, well, visible.
Unless it is marked private, protected.
When private then invisible. Protected has no meaning.
Yes it does it protects members from access by users, but allows access from
sub classes.
Subclass implementation is a user as any other. It is an important point if
you want to encapsulate properly. Only children of the package should have
access, independently on whether they derive or not. Consider adding new
functionality on the class level. Why should I derive anything to gain the
access? On the other side, let I derive from a type which implementation is
a black box to me. I don't want to know the implementation details.
C++ programmers have massive problems with deciding "private" vs.
"protected." The reason is obvious, this decision cannot be made by the
designer of the class. Forcing it too early is a problem. So they go:
#define private protected
I suggest this is why you wanted macros? (:-))
+ contracted exceptions
try, throw, catch, and finally.
I meant that each operation has a contract of the exceptions, which may
propagate out of it. The exception contracts are statically checked and
inherited from parent typed. (Java, but with lessons learned.)
Okay. Please elaborate.
Java lacks mechanism of working with these contracts. You should be able to
say: A can raise what B raises, or A does not raise if B does not etc.
+ statically checked pre-/postconditions, invariants
Yes like Eiffle, nice, I had though about these.
No, unlike Eiffel. Meyer did it wrong, IMO. In Eiffel they are assertions.
I meant correctness checks, i.e. everything happens before the run.
Otherwise it makes no sense.
Really you want both, compile time assertions and runtime ones if they are
not resolvable at compile time. Maybe they want to be separate syntatically
though.
I disagree. Run-time is evil, it is poor man's testing. One writes an
assertion and believes he solved the problem.
Run-time checks are conceptually wrong. What does a client when a check
fails? If that was a contract violation, it could do nothing, because the
program can be in ANY state. If the state is somehow defined, then that is
a part of the contract, like Overflow_Error or End_Of_File_Error. In that
case it is not a check, but a valid condition.
+ abstract interfaces for standard container types (array, record)
+ abstract interfaces for referential types
+ abstract interfaces for aggregates
+ dynamically constrained types
Runtime typing, runtime constraints ? Please clarify ?
Consider an array type, like string. String is unconstrained because its
length is unknown. But this does not mean you need dynamically allocated
strings like in C++. In 90% cases all string objects are constrained (have
known length) on the caller side. But the callee is works with an
unconstrained formal object. This is a very powerful model of type
parametrization without generics.
Pretty useless in this example,
No, it is an extremely useful one. It allows a function to return a string
on the *stack*. Try it in C++.
+ downward closures
please elaborate.
Passing a function as a parameter. The function carries its context with.
Please give an example.
Consider function Integrate that integrates another function on some
interval. The integrated function is passed as a parameter. Now consider:
N : Integer := Read_From_Keyboard;
function Power (X : Float) return Float is
begin
return X ** N:
end Power;
begin
Y := Integrate (0.0, 1.0, Power);
Power is a downward closure, it carries N with when passed to Integrate.
The value of N is unknown until run-time.
To me any function is virtual. I see no reason to have other kind of
functions. They breaks out of the model. If you have multiple dispatch,
free functions vanish.
Not necessarily ? Please elaborate.
I did.
+ an ability to have polymorphic objects with value semantics (follows
from OO 2)
Dont follow please explain your model properly.
See above.
Still dont understand.
Derive a new type from Boolean (Belnap's four-state logical value, for
instance). Override "and", "or" etc. Create an object of the specific type
you don't know before run-time if it Boolean or Belnap. Pass it to a
subprogram. Do you want it passed by reference? I don't.
Any contenders ?
We will forget about the contenders :)
The problem is that to make that list right is a huge problem. You forgot
the mist important thing, the requirements of how the above aspects shall
be implemented, Say you add to multiple dispatch and obvious
requirement: "no dispatch may fail at run time," and you get a serious problem, since
nobody, AFAIK, knows how to achieve that.
I was not aware of anything like this with multiple dispatch could produce
exceptions if there are undefined usages though.
Lisp's MOF seems to have implemented MD fine.
Could you please elaborate and maybe give an example please.
When you derive S from T and inherit some f, the language shall force you
to define all implementations of f for all possible combinations of the
parameters if it cannot invent an implementation by itself. Consider:
f : T x P
you derive S from T, now there are
f : T x P
-----------------------------
f : S x P (inherited)
let's derive R from P, now we have
f : T x P
f : S x P
-----------------------------
f : T x R (inherited)
f : S x R (inherited)
At any step, the compiler shall ensure that each slot in the dispatching
table of f is defined either per inheritance or else by the programmer. No
undefined slots.
Okay.
Now, derive S and R in two separately compiled modules. See the problem?
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
.
- Follow-Ups:
- Re: The Language I want
- From: Aaron Gray
- Re: The Language I want
- From: Aaron Gray
- Re: The Language I want
- References:
- The Language I want
- From: Aaron Gray
- Re: The Language I want
- From: Aaron Gray
- Re: The Language I want
- From: Dmitry A. Kazakov
- Re: The Language I want
- From: Aaron Gray
- Re: The Language I want
- From: Dmitry A. Kazakov
- Re: The Language I want
- From: Aaron Gray
- The Language I want
- Prev by Date: Re: What's lacking: a good intermediate form
- Next by Date: Re: What's lacking: a good intermediate form
- Previous by thread: Re: The Language I want
- Next by thread: Re: The Language I want
- Index(es):
Relevant Pages
|
Loading