Supplementary Notes on Magenta

Brian Connors
25 September 1999


There is a lot more to Magenta that what made it out to the net. This document takes you through some of the stuff I can remember and some of the stuff I'd have added if I had thought of it at the time.

A note on the syntax examples: They're not in BNF. <Angle-bracketed> items identify lexical tokens, while clauses between |bars| are optional parts of the specification. This applies to the original reference as well, but was not specified.

Towards a Function Syntax that Makes Sense

The feedthebear() example was probably the first code ever written in Magenta, but you'll notice that it doesn't have an obvious return value (for that, check the rewritten version in the ARM. The function syntax in Magenta is very bizarre, and I don't think I ever understood it for more than a week.

You can see a number of oddities in the syntax that likely wouldn't have been implemented:

If you throw in a javaish concept of exceptions, a proper function definition syntax looks something like
	function <id> ( <argdeclist> ) returns <typename> | punts <expr> <namedblock> |
As for a call... that's a little sticky, but it generally looks as you'd expect (but see Entry below). Arguments may be passed by value, by location (i.e. pointers), or by reference; the syntax is based on C++.

Forgot Something? Arrays in Magenta

Okay, I'll make this quick. Do it either C-style ( int [] [] ) or Basic-style ( int (x,y) ). Whatever works.

Control Flow (1999 Update)

I found out later that I was stealing liberally from Perl long before I'd ever learned it. This section replaces the original control flow section in the definition, since it's incomplete and begs rewriting anyway.

The curious thing about the control structures of Magenta is that they throw the block syntax into complete disarray. Named blocks are actually possible, though, and someone might have a use for a named for loop.

For the record, I'm redefining all control structures in terms of C/Perl syntax. Magenta was heading in that direction anyway.

For

for ( <initcond> ; <finalcond> ; <stepcond> | ; except <condition> |) <block>;
I should just apologize to the language police right now. Trying to create a Basic-type for loop with C syntax was cute, but even in a joke language anyone can agree that the C form is the Right Thing, being more flexible. Magenta was intended to be perverse and pointless, but one thing it wasn't was weak. This is my final offer and I'm sticking by it.

The new thing is the except clause. If there is an except clause, the clause's condition is examined and if met, the loop is advanced without executing the block. Could this be useful? I dunno...

While

while ( <condition> ) <block>;

Until

until ( <condition> ) <block>;
These two structures evaluate the condition at the top of the loop. They should be self-explanatory.

Do/While

do <block> while ( <condition> );

Do/Until

do <block> until ( <condition> );
Evaluate at the bottom of the loop. Again, self-explanatory.

If

if ( <condition> ) <block> | else <block> | 
if/else construction, just like C (though ideally the keyword elseif should be recognized, and maybe cut in half by the lexer for the parser's convenience).

Unless

unless ( <condition> ) <block> | else <block> | 
This is actually Perl's unless statement. Perl is a language hack's dream because there's a lot of random moderately useful garbage in there, and I wish it had been more prominent at the time so I could have stolen a few of its features for Magenta. Slipped in when I wasn't looking, it did.

Switch

switch <condition_variable> {
	case ( <condition> ) <block> ;
		...
	default <block> ;
}; 
This is a very interesting idea of what a switch or select construct could look like. C treats it as a sort of computed goto farm, allowing oddities such as Duff's Device. Magenta takes a different tack, allowing each case to have its own block and therefore its own scope. Interestingly, it would seem that we'd have to add ranges to the language as well, but that comes under expression syntax.

Magenta had two control structures directly related to its IPC capabilities as well.

During

during <lcondition> <block> ;
During watches a specific condition (theoretically anything, but in practice there'd be no point in using a during unless it was watching another task in the same program) and evaluates that condition, then executes its body until it tests the condition and finds it false. (There is no opposite construction paralelling while/until and if/unless; the only name I could come up with for it is despite and that would be really confusing because it doesn't mean much of anything.)

When

when <condition> <block> ;
I bet you didn't know Logo had concurrency features.

That (specifically, the Atari 8-bit implementation) is where when came from. When essentially blocks until the condition happens, then executes its block.

Finally

finally <block> ; 
One after-the-fact feature, generalized from Java, was the finally clause. It was part of the exception system in Java, but in Magenta it becomes a block of statements executed whenever a loop construct is exited. Interestingly, at the time it seemed like syntactic saccharine (i.e. cute but useless); looking back, however, it makes some sense to make anything in the finally clause share scope with the loop body that just exited, so you can play with something that was declared inside the block. Good for cleanup purposes, I suppose (you could maybe dump the contents of a variable local to the loop to something outside its scope for postmortem purposes).

Operators

The following table is all the operators in Magenta, arranged by precedence. I've added a few. Where necessary I've added commentary, tagged with footnotes; I've also revised the table to make it more Java-like. There will be differences; fortunately, nobody has implemented it yet so there shouldn't be problems.

operator	default meaning			direction		
------------------------------------------------------------------
()		force precedence
:		member				lr

-(unary)	negative			lr
!(unary)	not				lr
~(unary)	compl			lr
* 		pointer to (address)		rl
&		contents of			rl
<id>&		reference to	lr
sizeof		size of an object		rl

++(unary)	increment			operand->operator
--(unary)	decrement			operand->operator

&		bitwise AND			lr
|		bitwise OR			lr
^		bitwise XOR			lr
~		binary select(1)		lr
$		binary mingle(2)		lr

^:		exponentiation			lr

*		multiplication			lr
/		division			lr
% 		modulo				lr

+		addition			lr
-(binary)	subtraction			lr

<< 		shift left			lr
>>		shift right			lr
>>>		unsigned shift right		lr

<		less than			lr
<=		less than/equal to		lr
>		greater than			lr
>=		greater than/equal to		lr

==		comparison			lr
!= 		not equal to			lr

&&		logical and			lr
||		logical or			lr
^^ 		logical xor			lr
:= or =		assignment			rl
+=		add to variable			rl
-=		subtract from variable		rl
(also *=, /=, %=, &=, |=, ^=, ^^=, >>=, <<=, >>>, and so on)

,			comma			lr

footnotes

  1. Binary select is an Intercal construct. It works something like a shift instruction, in that you provide an operand (the expression on the left) and a bit selector (on the right), and it will return the value of the selected bit. Not too useless, I should think.
  2. Like select, mingle is an Intercal concept. It takes two integral values and shuffles their bits, ideally producing a value of double the length. Of dubious utility.
I'd like to have a range operator, too, something with a [ <start> .. <finish> ] syntax, though I'm not quite sure how it would work, except to say that comparing a value against a range would return a true value if it was in the range and a false value if it wasn't. Precedence? I don't know.

Variagents

What the hell is a variagent, anyway? Well, I dumped the reference manual through a program called MacTravesty one day and came up with one interesting word in the whole garbled mess that came out. I knew that Magenta would have to support variagents, but I didn't actually know what the bloody things were.

I finally decided that a variagent was simply an object method that was called as its own thread within the program. The syntax would look something like this:

	class bar extending foo is:
		<yada yada yada>
		
		variagent baz (int num) {
			<more yada yada yada>
		};
		
		<continuing in the yada vein>
	}

	end bar;
And then would be called somewhere in a program with

		baz(num);
Treating a variagent as a function would probably result in the parent task blocking until it got an answer, therefore negating the main reason for having the things in there in the first place. The convoluted priority syntax applies.

Magenta Packages

This turned into a prime example of not knowing when to quit. Magenta packages were a (supposedly) slightly more elaborate version of Java packages, with interface/implementation distinctions and recursive cascading subpackage visibility (I'll explain later). It was pretty evil.

Basically, a Magenta package could stand by itself or be part of another package. The interesting thing about Magenta subpackages that Java doesn't have is that the contents of a subpackage would be visible to the parent package, but not to sibling packages. The syntax would look something like


package <id> | of package <id> | is:
	<various and sundry declarations>
end <id>;

Alternately, there was a specific subpackage symbol that could be used to declare something in the x.y format, but looking back it's silly.

Packages never made it out to the public, at least not in this form. It seems that if you create this sort of cascading access format, you drastically complicate privacy/visibility issues. Suddenly there's no more protected class; you have to create individual protection levels for each level of nested packages whereby the children can see into the parents but not their siblings (but what if you want a child to be visible from a grandparent?). This gets extremely nasty after a while, and I don't even want to speculate what the syntax or semantics of that would look like. This is what I meant by recursive cascading subpackage visibility; think of an ice cube tray, I guess. You can fill the tray by pouring water into one cube slot (though it takes a while), but one cube is separate from another.

To top it all off, programs would be declared using the same syntax (with no nesting, obviously; that would be COBOL), only using the program keyword and requiring a main() function. The whole thing is unspeakably nasty, and I never finished a usable spec. The concept is intuitive enough in principle, but godawful ugly to put down in syntax.

Stupid Goto Tricks

There are a few control flow tricks I've thought of for Magenta. Some are new, some are old. Some are cool, but don't belong in the language.

Entry

I suppose it's significant that Dennis Ritchie could never figure out what to do with the entry keyword in C. Here's a solution:

	...
	r = foo:bar(42) entry baz;		//calls foo:bar()...
	...
	
	class foo is:
	...
		function bar(int n) returns int {
			...
		baz:	dosomething()		//and jumps to this label after 
						//initializing variables
			...
		}
	end foo;
What's going on here is that you have multiple entry points into one function. They're just labels, so they fall through. The clever bit is that they initialize any variables that should be in scope at the label; the probem is that you can't guarantee a variable will look as you expect it. Very ALTERish, in my opinion.

Alter

	alter <label> to <label> ;
Should be in there, should be deprecated. Labels are intended as specific (though retargetable) address references, an interesting side-effect of which is that alter could probably be implemented as a function that takes only pointers. However, it more likely requires some attention from the compiler. This is definitely syntactic saccharin, though; even so, though you might never see a goto in a Magenta program, you could do some very weird things with alter anyway.

Oh, and to clarify: what alter does, for those of you who don't know Cobol, is to alter the destination of a goto (or a comefrom, one might assume) to another label. In Magenta, the label that's altered is actually the destination/origination of a goto or comefrom at that point.

Come From/Resume

	comefrom <label> ;
	resume;
I think comefrom might have one or two legitimate uses, but it would require some very bizarre parsing tricks to work. There was some call for both comefrom and alter in the original discussions; this is the syntax. Come From in Magenta is intended not as a regular control construct but more a debugging aid:

function fred() is:
	...
	trapdoor: console:printf("Not here\n");
	...
	
	/* [?_DEBUG]		//remember the conditional compilation directives?
	comefrom trapdoor;		//just to separate report code, really
	stdout:printf("but here\n");
	resume;				//pop the stack and go back from whence 
					//you came
	*/
end fred;
More precisely, the semantics are like this:

  1. At compile time, bury a JSR-type instruction at the trapped label.
  2. Execute a goto from the trapped label to the comefrom.
  3. Execute code.
  4. At resume, pop the stack and continue at the point of the trapped label. If there is no resume, continue as normal.
If you want to see something really weird, try altering your comefrom routine and watch your carefully written debug code vanish into the Black Lagoon. It should meet many interesting Intercal programs there.

Gosub/Resume

Why not? Granted it's rather silly, but it can't hurt to have some way of remembering the source of a goto. The semantics for resume are the same as listed under comefrom.

Nonlocal Gotos

No way, no how. And I tried, too, before I realized that a) they were pretty much useless except for stack unwinds anyway and b) the semantics of doing a goto between functions were literally impossible.

Persistence

I threw a storage qualifier called persist into the language definition without any particular idea of how it was supposed to be handled. So here's how...

Dynamic Typing Features

As it happens, there was a lot of call for a more dynamic object model. In the grand tradition of design-by-committee, the dynamic folks get the isa and becomesa keyword.

	if (food isa zookeeper:arm) {
		bear:spit(food);
		food becomesa trash;
	}
The beauty of this is that you can see something to play with, but it won't quite work the way you want it to. It's very rudimentary, it's clumsy, it works with a considerable embarrassment factor; in short, it's a kludge, and it's perfect for Magenta.

Conclusion

I would love to see more features in here. Send them to me and the best will be posted on these pages; I'd especially like to see someone try creating a usable reference manual.


Click here to return to the Magenta page.
Click here to return to the Turing Tarpit. 1