Email list hosting service & mailing list manager


Re: Eq? issue Marcin 'Qrczak' Kowalczyk 07 Jan 2006 00:11 UTC

Andre van Tonder <xxxxxx@now.het.brown.edu> writes:

> Understood.  Is there a good reason to conflate eq?-behaviour with
> field mutability, though.

For comparison let me describe the design of type definitions in my
language Kogut. I feel it's a bit similar to SRFI-76, considering that
it was developed completely independently.

The common form of a type definition is:

   type <type-name> = <constructor-name> <parameters> {
      <definitions>
   };

Parameters are patterns (similarly to ML and Haskell, i.e.
an identifier matches any value and binds it to a constant).
Definitions are arbitrary local definitions: constants, functions,
mutable variables, lazy variables, dynamically scoped variables,
references with custom behavior, expressions executed for side
effects etc.

The constructor is a regular function. When it's invoked, it matches
arguments to parameters and executes definitions in the resulting
environment. Definitions are scoped like proposed LETREC* in Scheme.

The constructor returns an object with the given type tag, and
which makes available to the outside names bound by parameters and
definitions.

A pattern of the form 'var <name>' matches any value and creates a
mutable variable initialized to this value.

A pattern of the form 'private <pattern>' is like '<pattern>', except
that the names bound by the pattern are bound only in the appropriate
lexical environment, they're not made available outside the object.

A definition of the form 'private {<definitions>}' or 'private =>
<definitions>' splices the given definitions and similarly makes
names they introduce private. There is also 'public'.

A sequence of patterns may include '<pattern>...' once. The list
of the rest of arguments is matched against this pattern.

If the '{<definitions>}' part of the type definition is omitted,
it's equivalent to '{}'.

If the '{<definitions>}' part of the type definition is omitted
and all parameters are identifiers, the type is called a record type.
This has the following consequences:

* RECORD is added as a supertype.

* Generic functions for introspection are defined for this type:
  getting the constructor function from the object and getting the
  list of field names of the object. (Given an object and a field
  name, one can access the field.)

* The default equality is not reference equality but the equality
  induced by the equality of the corresponding fields. This is done
  by having a specialization of equality for two objects of type
  RECORD. The type may still define its own equality in any case.

* Similarly for hashing and showing as a string.

* The function Change is made available for this type. It takes an
  object and a sequence of 'name, value' pairs, and returns a new
  object of the same type, with the given fields of given values,
  and other fields taken from the object. It's implemented once for
  RECORD.

* There is a shortcut for making the type serializable.

If the '{<definitions>}' part of the type definition is omitted and
parameters are omitted, the type is a singleton type. The constructor
is not a function but a constant. Also:

* SINGLETON is added as a supertype.

* A generic function for introspection gives the name of the
  singleton. It's used by showing as a string.

* There is a shortcut for making the type serializable.

After the type name, one can specify supertypes. They are used only by
the dispatch of generic functions. It's the programmer's responsibility
to ensure that they make sense, they don't influence the representation
or behavior of the object.

A group of definitions may include 'extend <expression>' once. The
expression is evaluated. When accessing fields of the object being
constructed, any field not mentioned explicitly among parameters and
definitions is taken from this base object.

The private name 'this' is defined implicitly in the scope of the
definitions. It denotes the object being constructed. While the object
is under construction, it signals an error when one tries to access
its fields (definitions might not have fully executed yet).

If the object extends another object, the private name 'base' is
defined implicitly in the scope of the definitions, and it denotes
the object being extended.

There can be several cases of '<parameters> {<definitions>}' where the
structure of the object depends on parameters. This is rarely used,
it's mostly for completeness with multiple cases of a function.

There are facilities for composing an object from reusable pieces
instead of extending a complete object, which allows "methods" to call
other "methods" on 'this' polymorphically, but I won't describe them
here: they are too far from this SRFI.

A symbol which begins with an underscore is local to a module.
It allows to make fields which are available in functions external
to the object but not to anybody which has access to the object.

I tend to put only state variables among definitions, and any
functions operating on the record are external instead. A more
Smalltalkish style would define functions inside objects; note
that they would be physically duplicated for each object.

--
   __("<         Marcin Kowalczyk
   \__/       xxxxxx@knm.org.pl
    ^^     http://qrnik.knm.org.pl/~qrczak/