MOO-cows Mailing List Archive

[Subject Prev][Subject Next][Thread Prev][Thread Next][Subject Index][Thread Index]

Re: Ideas about data types (fwd)



Michael sent this to me, and then realized it should probably have gone to 
moo-cows.  I'll write a reply in a while.

------- Forwarded Message

Received: from research.att.com (research.research.att.com [135.205.32.20])
	by amontillado.research.att.com (8.8.5/8.8.5) with SMTP id RAA20152
	for <eostrom@issr.research.att.com>; Tue, 22 Jul 1997 17:05:18 -0400 (EDT)
Received: from castor.ipac.caltech.edu ([131.215.11.35]) by research; Tue Jul 
22 17:04:12 EDT 1997
Received: from laurel (laurel.ipac.caltech.edu [134.4.20.87]) 
	  by castor.ipac.caltech.edu (8.7.4/8.6.4)
	  with ESMTP id OAA00211
	  for <eostrom@research.att.com>; Tue, 22 Jul 1997 14:04:04 -0700 (PDT)
Received: (brundage@localhost) by laurel (8.6.8.1/8.6.4) id OAA01413; Tue, 22 
Jul 1997 14:02:43 -0700
Date: Tue, 22 Jul 1997 14:02:39 -0700 (PDT)
From: Michael Brundage <brundage@laurel.ipac.caltech.edu>
To: Erik Ostrom <eostrom@research.att.com>
Subject: Re: Ideas about data types 
In-Reply-To: <199707221914.PAA01663@radish.research.att.com>
Message-ID: <Pine.SOL.3.91.970722124833.1376A-100000@laurel>
MIME-Version: 1.0
Content-Type: TEXT/PLAIN; charset=US-ASCII
Content-Length: 11270

On Tue, 22 Jul 1997, Erik Ostrom wrote:
>     5 as boolean

I suggest that you use this "var as type" syntax; furthermore, let the 
type correspond to those values which are returned from typeof() -- so 
for example,  5 as STR,  5 as FLOAT

This type should be computable, so that, for example,

x = STR;
y = 5 as x;

results in y = "5"

Some type conversions will result in loss of information, such as
y = 5.3 as INT;
I don't know whether the programmer should be signalled to this; one 
could always add a flag to each variable and make some sort of function
like conversion_error() which would return a false value if no error 
occurred and would return an error message (possibly just a string) if 
one did occur.  For example,

y = 5 as INT;
if(conversion_error(y))
  "no conversion error, so this line is never reached";
endif
y = 5.3 as INT;
if(conversion_error(y))
  notify(player, conversion_error(y));
  "conversion_error() might indicate loss of precision or whatever here";
endif


Some types may not be convertable to certain other types.  For example, 
lists may not be convertable to integers. In this case, an error should 
be raised, say E_CONVERSION, to let the programmer know something is amiss.

> Many types won't need a conversion function or "literal" value syntax at all; 
> their values are generated by other means.

Although it is true that there may be no explicit literal syntax for 
types such as file handles, it is nevertheless true that there should be 
an orthogonal syntax for constructing objects of various types with 
particular values.  I don't want to have to write code that looks like:

  if(typeof(x) == LIST)
    y = toliteral(x);
  elseif(typeof(x) == FILE)
    y = open(x.filename, x.permissions);
  ...

My understanding of literals (and perhaps this is flawed) is that a 
literal value is not merely its representation in MOO code, but is a 
value from which the exact state of the value may be recreated.  So it 
works like serialization in Java -- to_literal() should have a reverse, 
from_literal().  from_literal() is implcitly understood to apply to 
"built-in" types such as lists, and the parser knows this.  But for new 
types, one should have to write this explicitly:

  x = to_literal(z);
  y = from_literal(x);
  if(y == z)
    "always true";

This is analogous to the problem encountered with builtin functions, 
where MOO code may fail to work because a "built-in" function may have 
been removed.  The work around was to introduce call_function(), which is 
a more general method of calling builtins and has the advantage that it 
doesn't break with dynamic functions.  So I propose that we add a 
from_literal() function which is the counterpart of call_function() for 
literals.

Furthermore, I recommend that literals always be strings (as they are now),
because this facilitates storing them in the DB and transporting them 
across the network.

Having non-printable data types would be bad, IMHO.  Custom data types 
should always provide a meaningful toliteral() that will allow their 
reconstruction using fromliteral().

> Note that in my scheme, this may not even be true (as a naive implementation 
> will simply parse it as a list and an identifier).  However, it IS necessary 
> to load the data type before loading _stored values_ of that type--for 
> example, in properties or in suspended tasks.
> [...] Types are also 
> given their own hooks for reading from and writing to the DB file; these use 
> the DB module's I/O mechanisms, rather than being directly string-based.
> This  creates a bit of difficulty if you have a database containing 
> values of a type that your server doesn't support.  I haven't 
> finalized a plan for that yet.

Another advantage to having explicit fromliteral() calls is that when the 
MOO encounters a data_type it doesn't know, it can substitute the 
literal, just as it currently substitutes call_function() for builtins it 
doesn't know.  Of course, the code won't run in a meaningful way, but at 
least it will still compile.  This can be useful if, for example, code is 
written to take advantage of new data types before they are actually 
added.

> I'm not planning on any implicit coercion, as you suggest.

Yay!

> Possibly.  I'm not sure it's necessary to overload the arithmetic operators, 
> but I suppose it'd be handy if one wanted to implement new kinds of numbers, 
> and as Nick pointed out, they are already overloaded, so what's a few more 
> meanings.

Ack.  Just don't make it too hard to use.  It would be nice to be able to 
do things like notify(player, "The time is "+time()) without having to 
manually wrap tostr() around everything, and this would have the 
additional advantage that if you ever switch your functions from 
returning ints to returning strs (or vice-versa) then code like
notify(player, "The time is "+someobj:time()) will still work. *BUT* 
overloaded operators can make it harder to catch bugs, even when they are 
restricted to operating on values of the same type.  Also, integer and 
floating additions are associative, but string and list additions 
presumably would not be.  So in a way this would change how + works...

> > I suppose that if the new datatype is a container type, that
> > it should be able to handle indexing of a variable like lists and strings
> > currently allow.
> 
> Yep.  This is planned but not yet implemented.

Cool!

May I also recommend that literals be able to have properties and methods?
For example, we currently have the notation
#10:someverb() which executes a verb on an object
and
#10.someproperty which accesses a property on an object

But modifying built-in types requires special builtin functions for each 
type -- so things become cluttered with bf's like listappend(), 
listdelete() -- and with custom types, I expect we'll see things like 
vectorappend(), setadd(), hashtableput() -- aaaaagh!

Better, I think would be to have verbs and properties on any data type, 
not just objects.  I suppose the difference is that (initially at least), 
types would not be able to inherit from other types, and one would not be 
able to change the verbs for a data type.  However, one would be able to 
execute verbs and get/set property values.

Then one can not only do some_str[5], some_list[5] and some_vector[5], but 
also some_string.length, some_list.length and some_vector.length.  
Instead of cluttering the global namespace with listappend() and so forth 
(possibly creating conflicts among different custom data types), one 
could just do  some_vector.append(some_element) or 
some_list.append(some_element) or some_string.append(some_string).

In this way, one could code naturally, as:

#42:copy_container_to_vector
"Takes any container and returns a vector copy of it"
{container} = args;
try
  len = container.length;
except e (E_PROPNF)
  raise(E_TYPE);
  "container isn't really a container if it doesn't let us get its length";
endtry
result = this:new_vector(len);
for i in [0..len]
  result[i] = container[i]
endfor
return result;


If data types do not have properties and methods we can work with in this 
fashion, then not only will data type writers have to write many built-in 
functions to accomodate their new data types (or else hack the 
pre-existing built-ins to correctly work with them), but we will have to 
special case our code to deal with each new type of builtin.  For 
example,the code above would have to start out

{container} = args;
len = this:length(container);

where #42:length would have special cases for every recognized type:

#42:length
{container} = args;
if(typeof(container)==list)
  return length(container);
elseif(typeof(container)==vector)
  return vectorlength(container);
elseif(typeof(container)==set)
  return setlength(container);
...

Yuck!

The only alternative is to wrap data types in objects (in order to use 
the objects verbs and to accomplish the same thing), but this defeats the 
whole purposes of new data types.

I would also like to have the equivalent of "class methods" for custom data 
types.  Otherwise, constructing new instances of data types with no 
in-MOO-code literal representation will be difficult.  This is somewhat 
mitigated if the data type has a stable literal representation that works 
with toliteral/fromliteral.  For example, suppose vectors have a literal 
representation "vector:{item1,item2,...,itemN}" where each item is a 
literal for that item.  I.e., the literal for a vector of a vector of 
length one containing the number 3 might look  like 
"vector:{\"vector:{3}\"}".  In this case, we could get a new vector with 
some length using:

#42:new_vector
  {size} = args;
  if(typeof(size)!=INT)
    raise(E_TYPE);
  endif
  return fromliteral("vector:"+toliteral($list_utils:make_list(size)));

or even
#42:new_vector
  {size} = args;
  if(typeof(size)!=INT)
    raise(E_TYPE);
  endif
  v = fromliteral("vector:{}");    " construct an empty vector";
  for i in [1..size]
    v:append(0);  " increase the vector to be the size we want";
  endfor
  return v;

But neither of these is as concise or efficient as just doing

#42:new_vector
  return VECTOR:new(10);


> > If the new datatype is a range type, I would expect the
> > for var in [literal..literal]  construct to allow you to iterate over
> > a sequence of literal values in that type.
> 
> Can you give an example of this?

This only works for (partially) ordered types, of course.  For totally 
ordered sets there is no problem, and it would work just like 0..3 or 
"a".."c".  For partially ordered ones, such as complex integers, there is 
the problem that the range may not be constructible- for example, 
from_literal("complex_int:1+3i")..from_literal("complex_int:1+5i") makes 
sense but 
from_literal("complex_int:2+3i")..from_literal("complex_int:1+5i") does not.

It's not clear to me that this functionality would be that super -- 
in fact, I have a hard time thinking up examples of useful ordered 
data types other than the ones we already have.  Note that ranges are 
only good for data types which are intrinsically ordered; they wouldn't 
be good for things like depth-first searching trees (the vertices are 
ordered *in the tree*, but by themselves have no inherent ordering).  
Even for things like permutations (or the complex integers above), there 
may be no canonical ordering (lexical, transposition, etc. for 
permutations).

> At the moment, I'm just using the string-valued name of the data type as a 
> return value for typeof().  I really don't want to add a new built-in 
> variable for each data type.  (The type name used in the conversion 
> expressions above  isn't really a variable; it's an identifier valid 
> only in the context of  conversion.

Just as long as typeof() returns the same kind of data for all types.
It would suck to have to special-case the built-in types in our MOO code.

>  There'll also be a way to convert to an arbitrary named type, 
> analogous to call_function() for functions.)

I thought this was what the "5 as some_type" notation was for?  Just make 
the expected type an expression instead of a literal, and you already can 
convert to any named type by using, for example,
  5 as #123:foo()




Cheers,

michael
brundage@ipac.caltech.edu


------- End of Forwarded Message




Home | Subject Index | Thread Index