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

polymorphic supertyping



On 2003.09.03 05:17 vkarvone@mappi.helsinki.fi wrote:
> 
> The [...] use of the term parametric polymorphism and the description that
> talks about a syntax for run-time parameterization of an object with a
> superclass sound somewhat odd to me and I'm not sure that I understand
> correctly what you mean. Could you perhaps clarify the wording? For
> example, as I understand it, an object is an instance of a class, therefore
> it is not entirely obvious what it means that an object is parameterized by
> a superclass.

I apologize for the odd wording.  Bento syntax is a synthesis of OO and 
unctional approaches, and I must admit that I don't know the proper
terminology for some of its features, including the way it handles 
subclassing. 

I don't know any better way to clarify it than to show you some Bento 
code, with lots of explanation.  Here goes.

The basic principle underlying Bento is the equivalence of objects
and functions.  Function definitions are equivalent to type 
definitions; calling a function is the same as instantiating an
object.  Imagine Java classes with no fields, and no methods other
than constructors.

The simplest sorts of definitions in Bento look a lot like Java
assignment statements or variable initializations:

    hello = "hello"
    int foo = 7

but nothing is assigned or initialized by these statements.  They 
are definitions of two new types, hello and foo.  hello is defined 
to be a type with no supertype, instances of which have the string
"hello" for their value.  foo is defined to be a subclass of int 
(a built in type), instances of which have the value 7.  Bento is 
lazy; no string or integer is allocated unless/until a hello or a 
foo is instantiated.

Instead of expressions, as above, types may be defined using
blocks:

    hello [| hello |]

    int foo [=
        int x = 7

        x;
    =]

As you can see, Bento has more than one kind of block.  The
delimiters [| and |] mark a data block; [= and =] mark a code 
block.  Once again, hello has no supertype and is always the 
string "hello".  foo is different, however.  It contains two 
things, a definition and an instantiation.  The definition is for 
a subclass of int named x; the instantiation happens to be of
an instance of x (the semicolon is the instantiation operator).
Instantiating a foo consists of instantiating a foo.x, which 
has the value 7.

Definitions can have parameters:

    int foo(int x) [=
        x;
    =]

In this version of foo, x is a parameter passed in when foo is 
instantiated, e.g.:

    foo(7);

Definitions can be overloaded, but not by adding new definitions 
with different signatures -- one of the basic principles of Bento 
is that there is only one namespace, and one definition at most of 
any name in any given lexical scope.  Instead, you add additional 
signatures, and additional code to handle the new signatures:

    int foo(int x), (float f) [=
        with (x) [=
            x;
        =] else [=
            (int) f;
	=]
    =]

The with keyword works like if except that it tests for presence 
rather than truth value, i.e., whether the named parameter was 
indeed passed in.

Since foo is a type, you can further subtype it: 

    foo bar [/]

This defines a type called bar which is a subtype of foo. [/] is an
empty block, meaning bar has no implementation of its own and simply 
inherits everything from foo.

Definitions can specify the parameters to be passed to the supertype
upon instantiation:

   foo(7) bar [/]

Parameter expressions passed to the supertype can include parameters 
passed to the subtype:

    foo(n) bar(int n) [/]

Multiple inheritance is supported by providing a list of supertypes:

    foo(n), fum bar(int n) [/]

A subtype inherits all child definitions (interface inheritance) 
from all its supertypes, but inherits its instantiation logic 
(implementation inheritance) from just one supertype, namely the 
first one in the list which is instantiable given the parameters:

    foo(n), fum(f) bar(int n), (float f) [/]

Given the above definition, bar inherits foo's implementation if
instantiated with an integer value, and fum's if instantiated with 
a floating point value.

This is what I mean by polymorphic supertyping -- probably not the
correct term.  What would you call it?

Michael

-----------------------------
Michael St. Hippolyte
http://www.bentodev.org