Once Programming Language

Once is a programming language I'm designing in my spare time. The best one sentence description I have so far is: "If Dylan is Common Lisp with a Pascal like syntax, then Once is Scheme with a C like syntax."

The name Once comes from the extreme programming principle OnceAndOnlyOnce, and several of the design decisions behind Once have their justification in that principle. Besides that, there isn't anything particularly extreme about Once.

Below I've listed some of the more novel features of Once. I'm listing them here for two reasons: to get feedback (Once is still a work in progress) and to encourage other people to steal them.

Interesting new syntactical features of Once:

[$ a = ["3 + 4 = [$ 3+4 $]\n"]; [# same as b = ["3 + 4 = 7\n"] #]
b = ["ls /proc = [`ls /proc`]\n"] $]
New semantic features of Once: Are there ways to control how far PartialEvaluation is taken? Otherwise there may be excessive code expansion.

-- ThomasColthurst


Lots of interesting ideas. Do you have larger code samples anywhere? -- SteveHowell


Agreed, there's some good stuff here. Some comments:

Unary minus is neg, as in y = neg x. In DM, functions all take just one argument. But that argument can be a tuple, which is what the comma operator forms. So you'll see things like y = f(a,b,c) which look like functions of more than one variable, but you could have also said x = (a,b,c); y = f x; . Binary operators are syntactic sugar for function calls: x = 1 + 2; is "really" x = _+ (1,2);

Nothing gets parsed like a ternary operator in DM. However, I'm considering adding some cleverness to the standard library to make things like "if .. _then .. [_else ..]" work. Gory details: _else (if it was part of the expression) would have the highest precedence and make an "else" tuple. _then would have the next highest precedence and make a "then" tuple out of the condition and its right argument (which might be an else tuple). "if" is then a normal function which demands a then tuple and does the correct thing with it. It remains to be seen whether all of this is worth it.

Not only are integer and floating point arithmetic considered distinct in DM, there are in fact many different integer and floating point (and even fixed point) types. There is IEEE32, IEEE64, INT8, INT16, INT32, INT64, and BIGINT, just to name a few. You can use the operators +, -, and * for any of these, or for any user defined type that implements the CommutativeRing? interface.

But none of this is "builtin" (to the interpreter, it is built into the standard library), so user code can do the same thing. In particular, there are two types of polymorphism in DM. There is "principled" polymorphism where you associate a set of functions and operators with an interface; they are then overloaded for every type that has an active implementation of that interface. (This is the kind of polymorphism used for arithmetic types). There is also "ad hoc" polymorphism in the sense that you can write a function that then dispatches on the type of its argument. DM encourages principled polymorphism over ad hoc polymorphism, and (hopefully) makes it the easier type of polymorphism to use, but ad hoc polymorphism is sometimes necessary (... in the standard library's implementation of principled polymorphism, for example!)

DM is (semi-)functional, so = is the name binding operator and it's left argument has to be a name. On the other hand, DM does have := which does over-writing assignment like setf, but :='s left argument has to have a box type: x = box 3; x := 4; While :='s left argument is allowed to be any value or expression with box type, it's not a GeneralizedReference. (Version 2.0 of DM may support AspectOrientedProgramming, which would then let you turn :='s into GeneralizedReference s, but in all honesty, even Version 1.0 of DM is a long way off right now.)

Requiring semantics for certain operators is mainly accomplished by associating those operators with an interface. (See above comment on principled polymorphism and below on DM's type system). I should perhaps mention that it isn't an ironclad requirement: when you register your implementation of SPIFFY_NEW_TYPE as a CommutativeRing?, DM doesn't actually check to see if a*(b+c) == a*b + a*c for all values; that's merely a property that CommutativeRing?'s documentation says is required and that functions which use CommutativeRings? are justified in assuming.

I'm not familiar with either Rebol or Xl, but I will take a look at them. Parze zones are not first class (but all non-comment parse zones return first class values). Yes, I mean DynamicTyping instead of WeakTyping. My bad. I've fixed it above. It's perhaps worth noting that there are no implicit conversions in DM, but there is a builtin function that will convert any value into an array of bytes. Faces are nothing more than interface implementations. Here's an example:
WeakOrder? = Interface [l _<= l];  [# <= should satisfy:  forall a,b : (a <= b) || (b <= a)
 forall a,b,c : (a <= b) && (b <= c) _implies (a <= c) #]
Register WeakOrder? INT64_LESS_THAN_OR_EQUAL_TO;  [# This is the standard WeakOrder? on integers #]
x = (5 <= 25);[# x = true #]

Register WeakOrder? INT64_DOESNT_DIVIDE; [# This is a nonstandard WeakOrder? on integers #] x = (5 <= 25);[# x = false #]

UnRegister? WeakOrder? INT64_DOESNT_DIVIDE; x = (2 <= 8);[# We are back to the standard one, so x = true #]

-- JonathanTang

Thanks for your comments and your excellent questions! -- ThomasColthurst


RE: "[...] there isn't anything particularly extreme about Once."

''You could use XP while writing Once. ;->


Once has been renamed to DM (short for Dead Mathematician). More information about it can be found in the paper http://www.hacksaw.org/~thomasc/dm_paper.ps --ThomasColthurst


I don't think that was like John Baez on acid. It sounded very much like John Baez like he usually is at a conference, except less talkative. And fewer references to category theory. :-) -- DougMerritt


EditText of this page (last edited June 7, 2004) or FindPage with title or text search