I have reposted, this time to comp.lang.lisp and comp.lang.scheme instead
of comp.lang.scheme and comp.lang.smalltalk.
at present I feel this is more appropriate for these groups.
I appriciate the comments I had recieved for the last version of the spec,
here is a slightly more updated version of the spec.
I am sorry if this is difficult to read, a lot of information was portrayed
using the formatting, which does not really carry over to usenet posts.
I am also aware of my bad use of capitalization, however I have not really
been motivated to go through the spec and fix it.
I have gone though and have indented sectoin headers if that is any help...
at present I am considering the design of a module system. also the
possibility of ':' as a special syntax for reference of members within
objects or environments.
the design is still somewhat fluid, and many issues still need to be
resolved.
------
THIS SPEC IS PRELIMINARY
INITIAL 2002-08-14/22
General
I will attempt to design a language. this will be a hybrid of a functional
and imperative object oriented language.
much of the design at present borrows from languages of the lisp family.
Basic Syntax
function args...
object args...
the first object on a line is either a function call or an object. in this
case object means either an identifier referring to an object or to a form
which will evaluate to an object.
any lower lines with a higher indentation are also considered as functional
arguments to a given function, thus functional arguments will naturally be
the last arguments to a function.
a line may be terminated with '\' indicating it is continued on the next
line. for the next line indentation is considered unimportant, however a
good apprach would be to indent it more than one would indent any following
subexpressions, or the same if none exist.
I know this is a bit of a kludge, but at present I lack much better...
parenthesis may also be used to indicate nested functional arguments on the
same line:
function ... (function args...) ...
also infix notation may be supported:
arg function
arg function args...
the infix notation will vary from prefix notation only in the position of
the function call, the operations will be the same in either case (except
in cases where the first element is an object).
however the order of operations at present will not be defined based on the
requirement that nested expressions require parenthesis...
in cases where it is possible for both prefix and infix evaluation to
occure, prefix will be the default. thus in this case prefix may be used to
clarify certain constructs.
in cases where the function can not easily be determined the first object
will be assumed to be the function.
[for now I may disallow infix notation due to issues in implementing a
compiler...]
consider:
'new object' vs. 'object new'. it would seem that new would be the default
in both, however in the second object could default to taking the 'new' as
an argument, thus calling it's 'new' function. in cases where a function is
being applied to an object, the function will come first.
at present I will define symbols to contain letters, numbers, or one of the
symbols ···@$%^*+-/<>=:?_', and will not be allowed to start with a number.
the exeption will be + or - followed by a number, in which case it is
considered as a sign identifier and a number.
I will define symbols to be case insensitive, and to have lower case as the
default. some symbols by convention will be written in upper case, but this
has no real effect on the language.
all parsable symbols would need to be seperated using spaces, this y:=x+1
would be interpreted as a single token, and thus would need to be written
as y := (x + 1).
'object this will have the same behavior as in lisp, ie: the object will
not be evaluated.
#\character will represent an individual character, character will be the
typeable character.
#\code will represent a character by it's code.
code will either be numeric (hex, required to start with a number), or a
special nmonic for the character.
in the case of numeric characters they will be under the unicode encoding.
all characters will be required to be seperated by a space, and any value
which consists of multiple characters will be a code.
#\a => 'a'
#\0A => '\n'
#\space => ' '
numbers and strings will follow the usual rules.
// will be used for line comments, with /* and */ for block comments. at
present I will not define it as a requirement that block comments be
nestable.
the language will be dynamically typed and lexically scoped, and I will
make the requirement that it be tail recursive.
conceptually there will only be a few primative types: numbers, characters,
arrays, conses, and environments.
there will be a type referred to as a cons, this will have 2 slots called
head and tail. this will be used largely for the construction of lists.
strings and symbols will be considered an array of characters.
(items...) will be used to represent a list.
a list will be composed of cons cells, each of which will have the data
item in head and the remainder of the list in tail.
another form is
(items... . item) which will represent a list with item placed in the final
tail.
() will be an empty list, this will be considered a value in itself.
#(items...) will represent a array containing the items.
constructs accepting multiple forms will evaluate left to right and will
return the value of the last form.
if an expression is opened, ie: with ( it is still required to be line
broken and ended with ).
ex:
(begin
display "1\n"
display "2\n")
is valid.
but:
(1+
2)
is not, and:
(1+ \
2)
is.
a requirement is that sub expressions on lower lines still need a higher
indentation than the parent line.
true and false will be values:
T and F, corresponding to true and false respecivly.
there will also be NULL, which will be considered distinct from ().
#t, #f, and #nil, will be considered a possible alternate syntax.
Definitions
definitions will be allowed within most code blocks, however within nested
blocks they are still required to preserve the top level semantics.
all nested defines will have a scope, this will include the remainder of
the enclosing form and any children within the remainder of the form. the
exception to this is redefine which may effect bindings outside the current
form (in the case of assignment).
define variable
define variable value
define (variables...)
define (variables...) values...
defines variable, and assigns value to it if a value is given.
define will only create a new binding but will not change any previous
ones.
in the case where it is given lists of variables or a variables list and
values it will bind multiple variables at the same time. the variables and
values are to be the same in number.
redefine ...
will have the same syntax as define however if the variable exists it will
be assigned rarther than creating a new binding.
defspecial variable
defspecial variable value
despecial (variables...)
despecial (variables...) values...
defines a special variable, special variables differ from normal ones in
that they follow dynamic scoping rules.
defining multiple special variables with the same name serves little
practical purpose as only the last defined value is used.
function name (args...)
forms...
function name args_lst
forms...
defines a function called name. multiple functions may be defined with the
same name in which case the last definition which matches will be used.
at present I will define that args may be either symbols or literals, where
a function match is considered if the values are equal and the number of
arguments is correct.
_ will be usable as a value which will allways match (except if the arg is
not present).
thus: (identifier = _) => T
having a symbol in the tail position, or having a symbol for an args list
will specify that the function accepts a variable number of arguments.
extra arguments will be passed to the function in the form of a list.
an example of how function might be implemented:
defmacro function (name args . forms)
`(define ,name
if (bound? ,name)
let (lambda-list
cons (lambda (,args)
,@forms)
get (last ,name) 'lambda-list)
lambda args
apply-match lambda-list args
let (lambda-list
lambda (,args)
,@forms)
lambda-list)
lambda (args...)
forms...
lambda args_lst
forms...
will create an anonymous function which accepts args, anonymous functions
have the advantage that they can be passed as variables without needing to
be bound.
let (variable value)
forms...
letrec (variable value)
forms...
let ((variable value)...)
forms...
letrec ((variable value)...)
forms...
will bind variable to value in each list and then invoke forms with the
variables bound.
in let the bindings are not in effect when each value is evaluated, however
in letrec they are, possibly allowing mutually recursive functions.
rlet name (variable value)
forms...
rlet name ((variable value)...)
forms...
this will be used for implementing a named let. name will be bound within
forms to refer to a lambda accepting the variables as values, and will be
initially called with the values bound as indicated.
this may be used for easy creation of recursive loops.
lazylet name ((variable value)...)
forms...
this will be a variation of named let. it will differ from named let in
that execution of the named closure does not yeild an immediate result,
rather it will return a lazy closure which may be stored in some structure.
on reference such a lazy closure will be evaluated and it's result will be
substituted in place.
as such use in the form of:
name args...
will return a lazy closure which will be evaluated on reference.
it may be useful in some cases to evaluate before storage (ie: when storing
into a variable). this may be useful in cases where checking for presence
in such fields will be considered too expensive. however it will not be
allowed for it to be evaluated recursively, as that would have the same
behavior as named let.
example:
define fiblst (list 1 1
lazylet rec ((x 1) (y 1))
cons (x + y) (rec y (x + y)))
Objects, Environments, And Closures
at present this aspect is still up for debate.
objects, environments, and closures will be considered roughly analogous.
an object will just be an environment with a few special functions bound.
an environment will just be a closure, with the ability to look into it's
bindings.
as such functions which may be used on one may generally be used on
another, thus it is possible to clone or exec code within a closure the
same as an object or environment.
however, the exact safety of tampering with closures can not be gueranteed,
for example introducing new bindings into a closure will not guerantee that
the closure will still work afterwards...
the structure will be that of a pair of variable dimensional trees, the
trees are to have matching structure however one contains symbols and the
other objects. any symbol denotes a leaf and thus indicates that spot on
the tree to contain data. I will define that either tuples or lists may
compose an object.
likely this will need to be optimized, however the exact implementation of
the environment is outside this spec.
this will use a variation of a prototype object system.
all the top level forms of an object will be the implicit constructor.
in cases where any "normal" operator is being used with the object the
object is to be used first in order to allow it to override the operator,
placing the operator in prefix position may be used to be more explicit.
General Object Minipulation
clone object
clones an object, by duplicating both lists. it does not call a
constructor, also it ignores any "clone proof" parts of the tree.
self, which will be bound on clone within the object to the resulting
object.
parent will also be bound to it's previous self.
one can check if an object is an ancestor of another first by comparing the
self value, then stepping along parent and comparing each one with the self
of the other.
an implementation is free to create both self and parent as builtin
variables, this may allow for more efficiency in some places (ie: checking
for ancestory, where one could implement parent as a list).
exec object ((variable value)...)
forms...
executes forms within the object's environment. this will be useful for
mutating the object.
any references within forms will be resolved to those which exist within
the object.
the (variable value) pairs may be used to bind some values within the
object, and thus exists as a means both to introduce new bindings or to
pass values to forms...
the introduced bindings are not gueranteed to disappear after the forms are
done.
another means would be to use dynamic variables to introduce bindings. in
this case it is the dynamic variables which would follow lexical
behavior...
object args...
[technical issues...]
when an object is used as a function it will work by looking up the symbol
indicated by the first arg, returning that value if it is not a closure or
object, otherwise it will apply it with the remaining args. prefixing the
last arg with a _ may be used to alter this behavior, in which case it will
just return the value rather than trying to apply it.
this is preferred means by which to interact with an object.
[this is inconsistent. the object is not a macro and thus will not be able
to accept unevaluated arguments. at present I will consider the invoke form
to be preferred.]
object (parents...)
forms...
defobject name (parents...)
forms...
this will work by creating a new object which will be the accumulation of
the current bindings and all the parents. after this it will invoke forms
within the new object. in the case of object the resulting object is
returned otherwise the result is undefined.
this is intended to behave much like a class in other systems, but may be
used to create other kinds of objects as well.
the last parent will dominate, each parent will dominate over the one
comming before. all of the parents are considered clone proof.
by default this will also include all bindings in place at the time of
definition, which will be considered as coming before all the others (and
thus will have the least influence).
Object Utility Bindings
current-bindings
which will return the current environment.
last symbol
last symbol num
can be used for backtracing symbols, or to implement overriding. also for
calling parent constructors, ...
using last on self may be used to access parent objects.
retrieve object symbol
will retrieve the value of symbol within object.
assign! object symbol value
will assign value to symbol within object.
bind! object symbol value
will bind value to symbol within object.
trace object symbol last
will lookup a variable within object and will return an object known as a
trace. on a subsequent call the trace may be passed via last to find a
previous binding.
for the initial call () is to be supplied in the last argument.
a trace will not be considered valid between environments or after a
destructive bind or exec...
retrieve-trace object trace
retrieve the value indicated by trace within object.
assign-trace! object trace value
assign the value indicated by trace within object.
invoke object func args...
this is similar to
object func args...
however the identity of the function is contained within func, allowing
invokation by name.
quote object
this is the longhand for 'object.
Basic Control Functions
if (test...)
then
if (test...)
then
else
evaluate test, if T then evaluate then, otherwise if present evaluate else.
cond
(predicate action)...
will go through each argument evaluating the predicate until one of them
returns T, when this happens action is evaluated and the result is the
result of the cond expression.
a final argument of T may be used to catch control if it is unhandled
otherwise, otherwise the result is undefined.
case key
((patterns) action)...
key is evaluated, then for each argument each pattern is compared against
the key. if it is a match then action is evaluated and the result is
returned.
_ as a pattern may be used to catch control, in which case action is
evaluated and the result returned. the result is undefined otherwise.
begin
forms...
will evaluate each form in order, the result of the last form is returned.
delay (form)
delay is used for returning a closure which will return the results of
evaluating form when applied. delay is intended to allow implementation of
lazy evaluation.
this may be considered synonomous with 'lambda () (form)'.
I will define that some list stepping functions 'ltail' will evaluate the
tail of a cons when called and it is a closure.
Operators
operators are not technically different from normal function calls. these
will be used in either the form 'operator a b' or 'a operator b'.
== the same object
= equal
> greater than
< less than
~= not equal
~== not the same object
<= less than or equal
>= greater than or equal
and both arguments are true
or either argument is true
xor only one argument is true
nor both are false
not accepts only 1 argument, returns T if a is F, F otherwise.
:= assignment, this destructivly modifies a given binding (a) to refer to a
new value (b).
Macros
like functions, macros will be allowed both within prefix and infix
positions.
macros will be defined with
defmacro name (args)
forms...
a macro is essentially a function which will take it's arguments
unevaluated. it will be called as if it were a normal function otherwise.
macros are intended to perform the task of interpreting a piece of code and
returning a form which will correspond to the value of the given macro
call.
macros will be like function calls in that they may also override previous
definitions, in which case the last one which is the closest match will be
used.
macros will be called with possibly the full power of the language
available, however it is likely that in some cases (ie: a static compiler)
they will be called with a potentially reduced form of the language.
because macros are code themselves any contained macro calls will be
expanded automatically.
I will define backquote, it is a means of constructing a list from a
template. it will be quite similar to the normal quote except that elements
prefixed by ',' will be evaluated normally, and those prefixed by ',@' will
return lists that will be substituted in place.
this syntax is annoying but at present I don't have much better.
example: `(1 2 ,(1 + 2) ,@'(4 5)) => (1 2 3 4 5)
Sequence Types
I will define a special propety of sequences: when used as functions they
will be implicitly indexed, and when an extra argument is supplied it will
be an assignment:
('(1 2 3) 1) => 2
(#(4 5 6) 2) => 6
naturally this will extend to strings:
("Hello" 0) => #\H
comparisons on sequences:
comparisons may be applied to sequences like other data types. on
comparison of a sequence it will work by making sure they have the same
length (in the case of =), and that the elements match the comparisons (all
need to hold true in case of =). greater or less comapisons will compare
either until the values differ in value or the end of the list is reached.
the leftmost elements will be most significant.
ci=, ci>, ci<, ci>=, ci<=, and ci~=
will be used on character arrays and will perform a case insensitive
comparison.
+ may be used to concatencate sequences as well.
(+ "Hello " "world\n") => "Hello world\n"
Lists And Expressions
both lists and expressions will be considered similar structures, and will
have similar functions for manipulation.
data lists will not be evaluated.
head cell
returns the head member (car in lisp) of the cell.
head (1 2 3) => 1
tail cell
returns the tail of the cell (the cdr in lisp).
tail (1 2 3) => (2 3)
when called and the tail is a closure (in the case of data lists), the
closure will be evaluated and substituted in place, and the result will be
returned. this is intended to ease implementation of lazy lists (or
streams).
[h/t...]lst cell
this is sort of like c[a/d...]r is lisp.
these are defined to be arbitrarily combined up to 4 levels (thus limiting
to 24).
ie: caddr => httlst, cdar => thlst, ...
ltail cell
this will behave like tail, but with an important exception: closures are
not evaluated if the tail is a closure.
cons head tail
this will create a new cell containing head and tail. this will be of the
list type
list args...
will compose a data list containing args.
list 1 2 3 => (1 2 3)
list-length lst
returns the length of the given list.
list-ref lst index
retrieve the value at the given index in the list.
list-set! lst index value
set the value at the given index in the list.
comparisons may be used on lists, in which case they will be recursive.
possible: it may be possible to allow lists of characters to be used as
strings. this would be grossly inefficient however it could also be useful.
Arrays
tuples will be a basic type. tuples will comprise strings, symbols, and
vectors.
arrays do not necissarily contain normal objects, they may be far more
restricted in many cases. the other types are thus specializations on
arrays.
strings and symbols will refer to arrays full of characters, no other type
will be allowed.
vectors will contain normal elements, and are considered as restricted as
such.
array-ref array index
this will refer to a given slot within a array.
array-set! array index value
will assign value to a given slot within a array.
array-length array
will return the length of the array in slots.
make-array type length fill
will create a new array with the given type and length, being filled with
fill...
types:
array-elem normal array filled with elements (a vector).
array-character the basis for string and symbol
array-string a string (strings and symbols may involve additional
metadata)
array-symbol a symbol (or may be types in themselves)
array-u1 bit array
array-u8 unsigned byte array
array-s8 signed byte array
array-u16 unsigned short array
array-s16 signed short array
array-u32 unsigned 32 bit int array
array-s32 signed 32 bit int array
array-u64 unsigned 64 bit int array
array-s64 signed 64 bit int array
array-float array of 32 bit floats (assumed ieee encoding)
array-double array of 64 bit doubles (same here)
string chars...
this will compose a string from the given chars.
symbol chars...
composes a symbol from the chars.
vector elems...
composes a vector from the given elements.
stepper array
this will return a closure referred to as a stepper. when called a stepper
will step along an array, reading elements 1 at a time until the end of the
array. it will return nil once the end is reached.
example implementation:
function stepper (a)
define x 0
lambda ()
if (>= x (array-length a)) nil
begin (:= x (+ x 1)) (a (- x 1))
one possible use for a stepper is to step along a string and read
characters.
--
<cr88192[at]hotmail[dot]com>
<http://bgb1.hypermart.net/>
I have so far added a few misc features. some minor alterations have also
been made to the list and array handling as well (defined associative
lists, added cat feature, more stuff relating to destructive modification,
and some minor function name changes).
objects have been changed to be accessed through an accessor function (for
several reasons). additionally I have done some minor design for more
runtime type features.
I am considering seperating objects from closures, and also have defined
that variables may contain extra "meta information", though as of yet I
have not defined the information...
I know this does not offer much particularly new, but I do appriciate
comments to indicate what I am doing well and what I am not.
------
Runtime
I will define that the runtime be persistent for a general program. there
may be times when this is not the case (ie: when the host system is
incapable of maintaining a consistent image).
Streams
conceptually all streams will be a connection between 2 points. something
written in one end will come out the other and vice versa. streams and
sockets may be implemented similarly...
the idea will be that streams will be composed of 3 parts: 2 ends and a
connection. initially an end will not be connected to anything, and thus
will not accept io (it may be that an end will just be used for it's
identity...).
typically the connection will be hidden, and interaction will be
accomplished through the ends.
this concept however may break down in many cases, ie: where the connection
in itself comprises the other end or the object of intrest is not another
end...
the connection will serve the purpose of getting data from one end to
another, but may also be defined to serve other purposes.
stream-end
will create a new end.
stream-connect end1 end2
will create a simple bidirectional connection between the 2 ends.
read-bytes stream count
will read the requested amount of bytes from the stream, and will normally
block if not enough are available (or will return immediatly if the stream
has ended).
this will return a byte array containing the read data.
write-bytes stream data
will write the requested bytes to stream. data will be a byte array
containing the structure.
bytes-ready stream
will return the number of bytes currently available in the stream (or the
largest block which can be read).
incomplete...
Message Passing
I will define that message passing will be achieved through a "socket".
sockets will be streams which will accept the data to be sent in the
message.
the data to be sent will be in the form of lists.
socket args...
will send args to the socket indicated by socket.
recieve socket
(<pattern>)
forms...
...
recieve-nb socket
(<pattern>)
forms...
...
accept a message from socket and try to match it against each pattern,
returning the result of evaluating forms for the first match.
if no match is found then the return value is unspecified.
pattern will have the same structure as that of function calls, however the
first argument will allways be a socket capable of recieving return
messages. before executing forms all values indicated in pattern will be
bound to the equivalent fields in the message.
recieve will block if no messages are available, however recieve-nb will
immediately return nil if no messages arrive on socket.
socket type args...
this will create a new socket. type will indicate the general protocol to
be used, and args will be used for protocol type specific args.
this will return the socket or nil on error.
connect addr
will connect to the system indicated by addr. at present if this is a
string it will be assumed to be a url.
this will return a socket, or nil if unable to open the requested
connection.
Modules
I will define a basic module system.
modules will by defualt placed in environments placed at top level. each
module will just be a specially defined environment.
modules will be created by binding them in their respective locations.
ex: bind! user-modules my-module some_mod
import module
will import the module defined by module. this will be a reference to an
environment, or a string in the case of external system modules.
module location name description
forms...
this will be a convienience function used for defining modules. location
will indicate where this module is to be created, name is what name this
will be bound under.
description will be a textual description of this module, possibly useful
when browsing modules to identify what functionality is defined.
export name ext-name description
will export a given binding from the module. name is the name of the
binding within the module, and ext-name is what it will be exported as.
description is a textual description of this export.
exports are to be located after their respective variables have been
defined.
Threads
threads will behave roughly as in other systems.
thread func
will create a new thread and executes func, the return value will be
derived from the last form and may be retrieved after the thread
terminates.
on the creator side this returns a thread descriptor.
threads will share the same bindings as the parent, it is the idea that a
thread will use dynamic variables for local storage. let or similar may
also be used.
wait-thread desc
will wait for thread to terminate and will return the return value of the
thread. this will return the result immediatly if the thread has allready
terminated.
kill-thread desc
will kill the indicated thread.
thread-terminated? desc
will return #t if the thread has terminated, #f otherwise.
--
<cr88192[at]hotmail[dot]com>
<http://bgb1.hypermart.net/>
cr88192 wrote:
> I have reposted, this time to comp.lang.lisp and comp.lang.scheme instead
> of comp.lang.scheme and comp.lang.smalltalk.
> at present I feel this is more appropriate for these groups.
Why do you feel you (or anyone else) needs your language? What defects do
you see in other languages that you wish to rectify with this language.
Without this base of information, there can be no "proper" evaluation of
this language.
What information do you want from this evaluation? Whether we think it is
technically sound? Whether we think it is aeshetically pleasing? Would we
be willing to use it?
Again, the desired information needs to be described.
> I appriciate the comments I had recieved for the last version of the spec,
> here is a slightly more updated version of the spec.
Well, without the information
> I am sorry if this is difficult to read, a lot of information was
> portrayed using the formatting, which does not really carry over to usenet
> posts. I am also aware of my bad use of capitalization, however I have not
> really been motivated to go through the spec and fix it.
If you are not motivated to fix the spec, why should we feel you are
motivated to construct a language and why should we care?
All this being said and myself being a charitable type, I did avail myself
of your specification and found the language excruciatingly ugly,
underpowered, and ill-defined compared to Common Lisp. There is nothing
here that has not been done better or more elegantly elsewhere. I see no
reason for this language to exist other than to stroke the ego of its
creator. If I were to make a recommendation, it would be for the language
designer to make a few existing languages better before he embarks on the
perilous task of creating his own. It may not be as satisfactory to his
ego, but it would be more useful and more likely to be accepted.
Hope this helps...
faa
cr88192 wrote:
> I am sorry if this is difficult to read, a lot of information was portrayed
> using the formatting, which does not really carry over to usenet posts.
> I am also aware of my bad use of capitalization, however I have not really
> been motivated to go through the spec and fix it.
No problem; but if you don't treat it as something that's
important enough to get right in the trivial matters, what's
the point of us expecting you care enough about it to try to
get it right in the substantial matters?
If you want feedback, respect the people you're asking for
feedback from by investing the time to polish it up before
you ask them to invest the time to review it. Fair's fair,
you shouldn't ask people to put in more effort than you do,
nor to care more about the correctness of your work than
you seem to.
<remainder snipped...>
Bear
····@sonic.net wrote:
> cr88192 wrote:
>
>> I am sorry if this is difficult to read, a lot of information was
>> portrayed using the formatting, which does not really carry over to
>> usenet posts. I am also aware of my bad use of capitalization, however I
>> have not really been motivated to go through the spec and fix it.
>
> No problem; but if you don't treat it as something that's
> important enough to get right in the trivial matters, what's
> the point of us expecting you care enough about it to try to
> get it right in the substantial matters?
>
> If you want feedback, respect the people you're asking for
> feedback from by investing the time to polish it up before
> you ask them to invest the time to review it. Fair's fair,
> you shouldn't ask people to put in more effort than you do,
> nor to care more about the correctness of your work than
> you seem to.
>
it is all crap anyways...
I have no buisness trying to design a language.
if it is worth anything for a scheme vm of mine I am implementing similar
environment and objects, sockets/pipes have been changed (and made the
fundamental basis of io).
anymore objects and environments are just snapshots of the current bindings
(much like closures), but vary in behavior. they are implemented as
vectors, and clone is just an alias for vector-copy.
object specific fields are to be established by creating new bindings after
clone (thus: new). still much cleanup work is needed.
objects are interacted with using messages (though I am not sure it is
conceptually sensible to recieve messages from objects).
I have gone over to making sockets (or pipes) 2 sided, and sending messages
to one end results in messages being available on the other.
conceptually at the other end something will sit and will handle messages
flowing through the pipe. it is reasonably straightforward in concept to
bind pipes together or to implement pools or whatever.
it is also straightforward in concept to connect pipes to objects, thus
allowing more remote object interaction.
file/stream io would be accomplished by sending io messages over a pipe
connected to the conceptual file.
--
<cr88192[at]hotmail[dot]com>
<http://bgb1.hypermart.net/>