dotgnu-visionaries
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Dogus (was: Re: [Visionaries] DG-Scheme)


From: Peter Minten
Subject: Dogus (was: Re: [Visionaries] DG-Scheme)
Date: Mon, 05 May 2003 16:36:32 +0200

Peter Minten wrote:
> 
> Peter Minten wrote:
> >
> > Peter Minten wrote:
> > >
> > > Peter Minten wrote:
> > > >
> > > > Peter Minten wrote:
> > > > >
> > > > > Hi folks,
> > > > >
> > > > > I did a little thought experiment today, making Scheme truly OO and 
> > > > > AO (Aspect
> > > > > Oriented), it turned out that's not so hard, if you add the idea of 
> > > > > metadata. A
> > > > > language that is flexible, OO, AO and has metadata would be the ideal 
> > > > > language
> > > > > for many webservices. I believe DG-Scheme (as I call it) meets those
> > > > > qualifications. What follows is an explanation about the specifics of 
> > > > > DG-Scheme
> > > > > (well, as far as I've managed to map them out in a couple of hours).
> > > >
> > > > Additions.
> > >
> > > Addendum.
> >
> > Yet Another Addendum
> 
> Dito

No, I'm not done yet ;-).

I believe a new name for the language is needed, DG-Scheme is moving so far from
Scheme that it would be confusing to use the word Scheme directly in it's name.
The best idea I got so far was Dogus (an abbreviation of DotGNU Scheme).

With the name change I'm also letting go of the goal of R5RS approximate
compatibility. The differences between Dogus and Scheme are getting to big,
attempts to provide any more than natural compatibility would result in legacy
kludges.

Scheme is cool, but confusing. Something which would help is syntaxic sugar.
R5RS uses syntaxic sugar sparingly, I don't. Ruby has tought me that syntaxic
sugar is a good thing, it makes a language easier to program in. The problem is
adding the syntaxic sugar in terms of already existing rules.

The most simple solution is to make the syntaxic sugar keywords basic constant
variables. This means however that it will be possible to shadow them, causing
strange effects, but this is the case for any basic function/macro (part of the
power of Scheme). An implementation should provide an option to emit warnings
when basic functions/macros are overriden.

Now to the definition of the syntaxic sugar:
(define then begin) -- then <=> begin
(define else begin) -- else <=> begin
(when arg0 . args) -> (,arg0 ,@args)

Note that some syntaxic sugar of R5RS has been removed: in cons (foo => bar) is
not valid since it's ugly. The word 'else' in cond has been replaced by default
because otherwise else would have two different meanings.

A good style of programming is something like:
(if (<= a b)
    (then (display "A is smaller or equal to B") 
          (newline))
    (else (display "A is greater then B")
          (newline)))
and
(cond (when (< a b)
            (display "A is smaller then B")
            (newline))
      (when (= a b)
            (display "A is equal to B")
            (newline))
      (when else
            (display "A is greater then B") 
            (newline)))
and
(case a
  (when (1)
        (display ("A = 1")
                 (newline)))
  (when (2)
        (display ("A = 2")
                 (newline)))
  (when else
        (display "A is not 1 or 2")
        (newline)))

The advantage of the use of these keywords is visual clarity. I use a few
tricks. In cond I use the fact that else = begin and begin = a primitive
procedure and all procedures are true. In case I cheat, instead of a list of
datums a single non-list expression may be used in the last clause of a case
statement, if this is the case the last clause statements are evaluated if no
match was found. The use of the word else is strictly not necessary, even #f
would do, but this is simply syntaxically nicer (syntaxically consitent, though
semantically a little kludgy).

It's irritating that Scheme doesn't have a return statement (well,
call-with-current-continuation can be used for it), I don't like that. Return
statements can always be easily implemented and more efficient then
call-with-current-continuation, since return statements (like exceptions) always
cause up in the stack. The syntax of the return statement is: (return value).
When the return statement is evaluated the current lambda expression stops and
returns value. 

Now to the exception mechnanism. Dogus has of course decent exception
classes (Guile uses symbols in exceptions, which I personally find rather ugly)
and a try-catch-finally mechanism (it's irritating to have try-catch and
try-finally but not the combination). This is the basic system:
(intercept-exceptions
 (try (expression1)
      (expression2)
      ...
      (expressionN))
 (catch exceptionclass1
        statements)
 (catch exceptionclass2
        statements)
 ...
 (catch exceptionclassN
        statements)
 (finally statements))

Note that 'intercept-exceptions' may be abbreviated to 'icex'.

The advantage of this setup is, again, visual clarity. Schemes often use
something like:
(try ((expression1) 
      (expression2)
      ...
      (expressionN))
  (catch expressiontype
         expressions)
  (finally expresssions))

But that actually says the catch part is part of the try, but that's not really
correct, you first try something and if that fails you catch, try and catch are
equivalent. In C# and co 'catch' always starts on the same column as the
associated try, which is much clearer than the classic Scheme approach.

Scheme has something weird called values that returns multiple values, but not
in a list or something like that. Dogus doesn't have that, containers like
Array and List are good enough for this.

A general rule is that all syntaxic sugar must be replacable by 'normal' scheme
code. The implication of this is that a.b can be written as (get-field a 'b). To
keep matters consitent (set! a.b c) can be written as (field-set a 'b c) which
expands to (set! (get-field a 'b) c), not that field-set does not modify any of
it's arguments and thus does not have a bang at the end. Metadata can already be
written in terms of fields (address@hidden <=> (a.meta b)) and thus does not 
need new
rules. References can be transformed like this: ^a <=> (ref a).

The reference mechanism is bound to create some confusion with implicit
references and Reference objects. The basic rule is however simple: the results
of expressions are always passed by reference. However if a = 5 then the
expression 'a' would return a reference to 5, this way pass-by-reference turns
into pass-by-value. The expression '^a' evaluates to (ref a) which evaluates to
a Reference object which is returned. Thus '^a' is a kind of pass-by-value of a
reference. Note that in 'a' a reference is passed, but the reference does not
exist as a Reference object until it's turned into one by (ref), ref turns
references into values.

A nice little question is what fields a Reference object has. The rule is that a
Reference object has the fields of the reference it represents. Another rule is
that a Reference object has no fields of it's own. A third rule is that a
Reference object has it's own reflection metadata, but all other metadata of it
belongs to the reference that it represents. This means that changing/defining
any non-system (system metadata is write and shadow protected) metadata of a
Reference results in changing the metadata (r-data) of the reference. Finally a
small change of plan: deref is now a global method (method of Global), it's no
longer an object method.

All objects have a get-field method which returns an implicit reference to the
value of a field, this method can be overridden to make shadow objects (like
Reference) possible.

Greetings,

Peter



reply via email to

[Prev in Thread] Current Thread [Next in Thread]