[Top][All Lists]

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

[Visionaries] DG-Scheme

From: Peter Minten
Subject: [Visionaries] DG-Scheme
Date: Tue, 22 Apr 2003 16:29:53 +0200

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

** Field reference:

In Scheme procedures are first-class objects, DG-Scheme also relies heavily on
this principle. The way to refer to an object field (or slot if you like GOOPS
terminology) is by using the good old dot operator: obj.field .

** Metadata reference:

Every object has metadata. Object metadata is accessible using the notation:
address@hidden, this returns the value of the metadata field 'field'. Object 
is also settable using that notation: (set! address@hidden newvalue).

** Modules:

DG-Scheme has namespaces, called modules (GOOPS terminology). A module is
defined using the define-module construct: (define-module modulename
implementation). For example:
(define-module NewModule
  (define (sayHello)
    (display "Hello World!")

  (define somevariable "Hi!"))

The contents of a module can be accessed either by absolute reference:
(NewModule:sayHello), or by importing the module into the scope: (import-module
NewModule) (sayHello). The module divider token is : . Modules can of course be
nested. It's even possible to define a module directly as part of another one
(instead of nesting): (define-module foo:bar ...).

If no module is specified all defined stuff goes into the 'global' module.

Modules can be defined as private using (define-private-module modulename
implementation), in that case they are not visible outside the scope in which
they are defined (private submodules).

** Classes:

DG-Scheme classes are defined using (define-class name contents). For example:
(define-class MyClass
  (define b 10)
  (define (c d e) (* d e)))

** Metadata definition:

Metadata is defined by putting keyword . value stuff between square braces ([ ])
in a define. For example:
(define a 10 [constant]) defines a variable a with initial value 10 that is
constant (it can't be set!, however (define a 20) would work (that does not
change the value of a, it only shadows it)). 

The previous example didn't have a value for the metadata (well actually it has,
default value is #t), this one does:
(define b (* 3.0 (expt 10 8))
  [description "Contains the approximate speed of light in m/s"])

This definition actually expands to:
(define b (* 3.0 (expt 10 8)))

(define address@hidden "Contains the approximate speed of light in m/s")

Anonymous lambda definitions are of course a little tricky, since you can't use
the name of it, but 'this' helps: (lambda () [description "Returns false"] #f)
expands to: (lambda () (set! address@hidden "Returns false"). This works for
non-anonymous lambda definitions (every function definition).

The only unusuable case is that of the anonymous non-procedure object, but I
doubt that will pose a problem :-).

A word about style. In a function definition the metadata goes between the
lambda list and the implementation. In a variable definition the metadata goes
after the value.

It's not possible to group subattributes like in C# ([WebService (Name = "foo",
Description = "bar")]). It is however possible to emulate that (which is done
with C# attributes that come into DG-Scheme btw): [WebService-Name "foo"]
[WebService-Description "bar"]. The rule is simple, attribute-subattribute. One
must however reserve the bar sign for this purpose in attributes (DG-Scheme does
not check on this since it's a recommendation).

** Accessors:

For every value (including metadata values) there is a getter and a setter. If
the getter is #f the value is gotten directly (using a kind of implicit (lambda
() varname)), if the setter is #f the value is set directly (using a kind of
implicit (set! varname value)). The getter and setter are called @get and @set
and thus stored in the metadata. By overriding the getter and the setter it's
possible to implement accessors.

Another kind of accessor, one that does no checks but exposes another name to
the outside world than to the inside world (the kind that you have to code
manually in C# using get {} and set {}) is creatable with a simple statement:
(define-accessor name target). This create a variable name with value #f who's
accessors point to target. The read-only and write only variants can be produced
with define-getter and define-setter, same arguments.

** Generic methods:

It's possible to overload DG-Scheme methods. This actually changes the method
definition to another one which has the original method definitions in it's
metadata and selects the one that conforms best to the type of the given
arguments. Such 'Generic methods' are created when you use a two element list
instead of a name in a lambda list: (lambda (a (Foo c))) instead of (lambda (a
c)). The second method would accept any value for c, the first only an object of
type Foo, or derivates of type Foo. When there are two or more fitting variants
the one that fits best (is most specialized) is chosen. If in such a situation
two or more variants are equally specialized (variant a fits perfectly on
argument 1, but uses the superclass of argument 2 in the argument 2 slot;
variant b first perfectly on argument 2, but uses the superclass of argument 1
in the argument 1 slot) the one that fits best for the first arguments is chosen
and a warning is emitted.

It's also possible to vary in length with the argument list.

** Danger level:

The error reporting scheme of DG-Scheme is based on danger levels. There are
five danger levels, 1 to 10. The most serious errors are reported at level 1,
the least serious errors at level 10. What errors are logged (if a logger is
present) is decided by the *log-level* variable, default 8 (level 8 and up are
logged). What errors cause the program to throw exceptions is decided by the
*exception-level* global variable, default 5. What errors cause the program to
crash immediately is decided by the *crash-level* global variable, default 2.
Note that levels 10, 9, 8 and 7 are not really an error levels, they're more log

** Visibility:

The rule is that all defines (define, define-class, define-module) are public by
default. However by changing the @defaultchildvisibility attribute of a module
or class the defaults for the contents can be changed. For that reason there are
always 4 version of a define: normal define, public define
(define-public-class), protected define (define-protected-module) and private
define (define-private).

** Foreign Function Interface:

DG-Scheme supports a so called Foreign Function Interface, a way to interact
with other languages. The FFI will at least support C, IL and Parrot. Thanks to
the FFI it will be possible to use stuff written in C# in DG-Scheme. 

The way to import non-DG-Scheme code does not differ from the way to import
DG-Scheme code:

(load "mscorlib.dll") ;;Simple loading of DLL's, all stuff available

(load "mscorlib.dll"
      :import "String") ;;Selective loading of one class

(load "mscorlib.dll"
      :import "Net") ;;Selective loading of one module/namespace
                     ;;without submodule (Socket)

(load "mscorlib.dll"
      :import "Net.*") ;;Selective loading of one module/namespace
                       ;;with submodules

(load "mscorlib.dll"
      :import "Net.*"
      :dont-import "Net.WebClient") ;;Selective loading of one
                                    ;;module/namespace with submodules
                                    ;;but without a certain class

(load "mscorlib.dll"
      :import-regex "N[a-z]") ;;Selective loading of one
                              ;;module/namespace without submodules
                              ;;(the dot is not matched) using regular

Example of usage:
(load "mscorlib.dll" 
  :import "String")
(define a ( "foo"))
(set! a a.subString(1))

Note that there will be no implicit conversion between DG-Scheme types and C#
types. Explicit conversion will be possible however.

** DotGNU integration:

DG-Scheme will have it's own baselib, which contains the same functionality as
that of the general DotGNU baselib (but not in the same setup, IMHO the ECMA
baselib is badly designed). Some general stuff may also be coded in DG-Scheme
(WebServices code and other stuff that's so important that it justifies extra
effort to implement it natively). For most other things the DotGNU libs will be
used. I say most other things since for example a math package can better be
implemented in DG-Scheme because Scheme is better fitted for math than the C
family of languages (Scheme has exact rationals and infinite integers). Stuff
like a GUI toolkit lib is better implemented in C# since that's probably already
supported and it doesn't matter if integers get truncated.

** Use of existing Scheme code:

When Scheme was designed two crucial mistakes were made: no standard library
specification was created and stuff that wasn't unanymously (!) supported in the
council that created the standard wasn't standardized. As a result the Scheme
implementations vary widely and so do the libraries.

DG-Scheme will go it's own way and standardize it, though DG-Scheme will be as
compliant as possible (some characters that R5RS reserves are used) with R5RS.



reply via email to

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