[Grace-core] Language Questions, and A little bit of the String library

Andrew P. Black black at cs.pdx.edu
Wed May 25 10:14:48 PDT 2011


On 25 May 2011, at 0:25, Kim Bruce wrote:

> Actually, I forgot -- our interpreter also uses "++" for concatenation.
> 
> Kim

This is, of course, a feature of the library, not of the interpreter.   Clearly my memory is going.  I thought that you, Kim, objected to using + or ++ for string concatenation because + looks like a commutative operator.

I have no strong opinions on what operator symbol we use for string concatenation.  We might even have more than 1.  Fortress has four, as I recall, for simplicity in constructing output:

juxtaposition (or ||)  for concatenation
||| for concatenation with a separating space between the parts, when appropriate.
/// for concatenation with a separating newline between the parts, and
//// for concatenation with two separating newlines.
 
> 
> On May 24, 2011, at 9:10 PM, Michael Homer wrote:
> 
>> On Wed, May 25, 2011 at 1:09 PM, Kim Bruce <kim at cs.pomona.edu> wrote:
>>> On May 21, 2011, at 8:48 AM, Andrew P. Black wrote:
>>> 
>>>> Folks,
>>>> 
>>>> While on a train, with a few hours to kill (and electricity!) I started writing a little bit of Grace code — a start at the String library — because I need more example code to test the parser.  My code is attached.
>>>> || for String concatenation?
>>>> 
>>>> I wanted to use // for integer division.  But we have already taken it to mean "comment".    Switch to "--" for comments?
>>> 
>>> Both fine for me, but we should try to lock these minor syntax points up fairly rapidly.

That's why I sent the email.   

>> As used there it also appears to be a coercing concatenation ("Pi = "
>> ++ pi), rather than String ++ String. I have implemented it so far
>> with automatic coercion by asString from any object.

This was not in my code, as far as I know.  We had decided pretty definitely NOT to provide any _automatic_ coercions in Grace.   
We could make the implementation of ++ on String make the asString request to its argument.     We could also define ++ on the Grace ( Any?) object, thusly

	method ++ other:Any {
		self.asString ++ other.asString }

I'm not sure whether any of this is a good idea.  It does make output code less messy.

Other issues in this email chain, which are actual language design questions, are as follows:

1. Can we say

		const (a, b, c) := method that answers a tuple

I think that we have to allow this.  

2.  Are the parens necessary?  I think that

		const a, b, c := method that answers a tuple

parses unambiguously, but maybe it's harder to read?

3.  raise <exception object> for raising exceptions?  Or signal?  Or throw (I don't like throw, it makes me think of, well, I would rather not say.)

4.  I wanted an enumeration type TotalComparison = greater | less | equal. I defined it as three objects and a comprises clause.   This is a type issue, which we know that we haven't yet talked about.   Thoughts?

Since I asked that I've thought about more about types.   Perhaps it is reasonable to allow the use of any object or class where a type is required, in which case the object is treated as meaning (static type of object) and the class is treated as (static type of an object that the class's new method returns).  There is of course an ambiguity here: which objects are treated as classes.  Any object with a new method seems like the reasonable answer.  The other possible answer might be "any object that is defined with the class syntax".

The reason that I don't like "any object that is defined with the class syntax" is because this is intensional rather than extensional.   Two objects with the same properties (they are egal) ought to be interchangeable, regardless of the mechanism used to create them.   Yes, it is possible to write a class P with a new method that answers one of two different objects depending on the type of an argument.  But we will have to give P.new() a type — either a join type or a sum type — in any case, so the "type meaning"  of P is still well-defined.

5. Kim raised some additional issues wrt tuples.  He wrote

> I believe we are going to have type-checking problems with [tuples], as a 4-tuple is generally treated as a different type than a 3-tuple.  One allows access to the fourth element, the other not.  Hence we generally use lists, which are treated as variable length -- leading to my previous question about primitives.  If we allowed syntax like the Scala syntax above, then the use of Int* enables syntactic sugar for lists.

I agree that a 3-tuple and a 4-tuple are different.  Moreover, they can have non-homogeneous element types.    However, I thought that we could also allow a tuple with a variable number of elements, which would need homogenous element types in the variable part, and which would be accessed using the same interface as an (mutable) sequence. 
>> 
>> As for the parameter declaration syntax, (a, b, c) is a tuple with exactly three args.  What about   (a, b, c…) for two or more args, (a, b…) for one or more args, and (a…) for zero or more args?

> My intuition in looking at your notation would have said the first was 3 or more arguments, with the others two and one or more.  In general, I assume we would only put at most one more variable argument parameters in a declaration and to always have it at the end (otherwise we don't know where to end it).

Lets try it with types.  (a:Person, b:Address, c …:PhoneNumber).  This would mean two or more values: a Person object (no optional), an Address object (not Optional), and zero or more Phone numbers.   c.size would answer the number of PhoneNumbers provided, and they could be accessed using c at: 0 of foreach c do {num -> ...}

I don't feel strongly about using … for the variable argument symbol.  It has the drawback that in examples, we tend to use … as a meta-symbol meaning "more code here", as I just did.   c* would be OK, but might confuse people moving to or from c-family languages.

6.  Kim wrote:  I think Arrays/Vectors/ArrayLists need to be primitive if we want O(1) access.

Smalltalk does not have arrays as primitive, but does provide O(1) access to array elements.  The primitive n Smalltalk is allocating an object with a variable (indexed) number of slots.   However, it's not clear how to transfer this idea to a statically-typed universe.    Perhaps that is why Java made Array primitive.  Unfortunately, the Java design requires a dynamic type check every time one stores into an array, so if that was the rationale, something went wrong!  Java's choice also means that something like a string, which is represented by two numbers and an array, requires two separate allocations and an additional pointer dereference.    

I know what the "right answer" is, but this is probably worth some detailed discussion.   We have said that efficiency is not important, but I think that we still want arrays to be constant time!  It would be nice to have a  better answer than Java.

	Andrew




More information about the Grace-core mailing list