Thinking about my "object-oriented equational rewrite rules" and IRC bots. > http://lists.canonical.org/pipermail/kragen-tol/2007-March/000855.html
Suppose we think of definitions like c = (f - 32) * 5 / 9 as defining properties that exist on every object that meets their qualifications (and isn't shadowed by some previously-existing c). Then we could think of something like this: todayweather = { f: 95, windspeed: 37 } as defining a property 'todayweather' that exists on every object. And then you could plausibly ask, on an object that has only the properties that all objects have (the Ur-Object), what are the properties that have a c property? c? { todayweather: 36.1, normalweather: 25 } Namespaces ---------- If you're doing this on an IRC bot on a casual channel, you could have a namespace per nick, and only enumerate properties in your own namespace. So there might be [kragen]c and [Isomer]c; but I could refer to properties from other namespaces explicitly if I wanted to; simply importing: f = [isomer]f or using without importing: c = ([isomer]f - 32) * 5 / 9 Actual Programs --------------- Let's hold off for now on any way to make these properties (as opposed to their value on a particular object) first-class in the language. But let's have self-quoting keywords, like in Prolog or Erlang (lower-case), or Common Lisp or Ruby (with colons). I'm pretty sure that if I use lower-case for auto-quoting, I'll end up with Very Important-Looking Programs, so I'm thinking I'll use backquote (`) instead. Let's start with a little syntactic sugar for lists. Let's say that if we have some semicolon-terminated expressions inside parentheses, with the last semicolon optional, it is syntactic sugar for a list made of conses: ( a; ) => { x: a, y: `nil } ( a; b; c ) => { x: a, y: { x: b, y: {x: c, y: `nil } } } () => `nil Even without first-class properties, we can still write: (`nplusone; n).apply = n + 1 such that we can write (`nplusone; 3).apply and get 4. Given that, we can write map: (fn; ()).map = () (fn; {x, y}).map = {x: (fn; x).apply, y: (fn; y).map} And filter: (fn; ()).filter = () (fn; {x, y}).filter = (fn; x; (fn; x).apply; y).filter2 (fn; x; `t; y).filter2 = { x: x, y: (fn; y).filter } (fn; x; `f; y).filter2 = (fn; y).filter It's pretty brutally obvious that we need some special syntax for cons here. Let's try infix @. (fn; ()).map = () (fn; x @ y).map = (fn; x).apply @ (fn; y).map (fn; ()).filter = () (fn; x @ y).filter = (fn; x; (fn; x).apply; (fn; y).filter).filter2 (fn; x; `t; y).filter2 = x @ y (fn; x; `f; y).filter2 = y ().length = 0 length = 1 + y.length x.reverse = ((); x).reverse2 (a; ()).reverse2 = a (a; x @ y).reverse2 = (x @ a; y).reverse2 That works OK, and I could imagine people typing it in IRC. It would probably be good to eliminate the argument-order dependencies as much as possible, and reduce the number of properties defined on all lists of length two or three. In the below, {foo, bar} is short for {foo: foo, bar: bar}, and any free variables are required properties of the object the property is defined on. {list: ()}.map = () map = (fn; list.x).apply @ {fn, list: list.y}.map {list: ()}.filter = () {list: x @ y}.filter = {fn, x, include: (fn; x).apply, y: {fn, list: y}.filter}.filter2 {include: `t}.filter2 = x @ y {include: `f}.filter2 = y ().length = 0 length = 1 + y.length x.reverse = { reversed: (), left: x }.reverse2 {left: ()}.reverse2 = reversed {left: x @ y}.reverse2 = { reversed: x @ reversed, left: y }.reverse2 Those aren't quite so brief, but they're still within the length where people could plausibly type them in a conversation. So how about strings? Let's suppose that strings support the interface of lists of one-character strings, work properly in pattern-matching, and that one-character strings additionally have a .ord property that tells you their ASCII code. Can we split words on spaces? " ".space? = `t {ord}.space? = `f ({space?: `t} @ y).words = y.words ({space?: `f, x} @ y).words = {wletters: (x;), left: y}.word {left: {space?: `t} @ rest}.word = wletters.reverse @ rest.words {left: x @ rest}.word = {wletters: x @ wletters, left: rest}.word {left: ()}.word = (wletters.reverse;) That's not too bad. It compares favorably to Scheme in total code volume: (define (words string) (words-of-list (string->list string))) (define (words-of-list clst) (map list->string (words-list clst))) (define (words-list clst) (let ((x (car clst)) (y (cdr clst))) (if (char-whitespace? x) (words-list y) (word (list x) y)))) (define (word wletters left) (cond ((null? left) (list (reverse wletters))) ((char-whitespace? (car left)) (cons (reverse wletters) (words-list (cdr left)))) (else (word (cons (car left) wletters) (cdr left))))) The Scheme is 10 lines, 59 words, 498 characters, in place of 7 lines, 47 words, 299 characters. So from there I think you could quite reasonably, e.g. implement ternary trees and compute a time-efficient inverted index of a big bag of strings. Say, old chat lines, or a small number of HTML documents. Precedence ---------- When you have more than one rule that's applicable to finding a property value, you have to decide what to do. Should you use the first rule, use the second rule, or combine them somehow? Aardappel used specificity ordering. I think you should do the same here, given that the "source code" is a bunch of people chatting. But it would probably be helpful if the specificity ordering were partial, so that the users would have to resolve potential conflicts manually. Syntax ------ The question of syntax is a little ugly but probably soluble. The normal namespace syntaxes are as follows: Syntax Precedents Why Not isomer/f Unix used for division isomer\f MS-DOS painful memories isomer.f C++, Java, Python used for property access isomer:f MacOS, XML used for property definition isomer::f C++, Perl too verbose isomer f Smalltalk not verbose enough [ISOMER]F VMS forgotten isomer'f Ada, Perl4 painful memories, unbalanced ' isomer$f R, DCL painful memories, ugly There are some other alternatives, especially if we involve Latin-1: isomer|f isomer»f isomer«f isomer§f isomerºf isomer->f isomer>f isomer¦f isomer×f isomer÷f isomer¬f isomer®f isomer©f isomer±f isomer£f isomer·f isomer=>f isomer=-f isomer:-f isomer--f isomer-)f isomer-»f isomer°f isomer*f isomer~f isomer!f isomer[]f isomer()f <isomer f> <isomer>f isomer<f> isomer_f @isomer.f