June 3, 2005, 9:40:49 am
I thought I'd share some programming language thoughts with you. Ignoring the sound advice that you should never build a language unless you have a problem to solve with it, I'm going to explain some of my thoughts behind what I'd want a new "Next generation after Smalltalk" language to do.
I firmly believe in "less is more", so long as it's the right kind of less. There are lots of languages out there that declare types for instance variables. Smalltalk doesn't - but it does make you declare instance variables. This, I think, is a bad thing. It means that the shape of a class must be "overridden" if you want to "extend" it to do more jobs. So my first change to Smalltalk would be to remove instance variable declaration.
I know what you're thinking/saying - you can't do that, you'll make typo's and make two instance variables slightly named differently. There are a couple of answers to this: (a) write unit tests and (b) use an environment that will helpfully suggest that perhaps you have misspelt something that already exists.
Moving on from there, we realise we still need to declare local variables - otherwise they become instance variables which wouldn't be so good. So I'll leave that alone for now.
Next on my agenda is message sends and variable accesses. I like what Self did - it unified variable access and message sends in to one single concept - message sends. You can do this by stating that methods are variables filled in with a compiledmethod. When you 'access' the variable you are actually running the code it contains. This is a very Self-ish sort of scenario, where you have slots that you fill with 'stuff'.
If we can put methods in any ol' slot, we don't need classes any more. We can stick to just plain objects. But you'll need some way to do inheritance. Self did this by having slots that start with a * act as inheriting slots. This isn't a bad approach and it allows multiple inheritance. The problem with MI is that it introduces ambiguity. So let's borrow a page from the book of DoesNotUnderstand and add a new exception called AmbiguiseMessageSend. This allows us to intercept it if we need to.
An ambiguous send happens when non-shared roots of multiple inheriting paths offer different compiledmethod's for the same lookup. That's a mouthful and I won't try to explain it further at this point. Suffice-th to say that you can always know if a call will be ambiguous or not.
So any message send could call some new code or return some value stored in a slot. This means we no longer have to write "getter" methods for our code. Next we need a way to do "setters". I'm going to invent a new syntax here and say that := can be used on the end of a message send to mean "assign to that slot". Eg: person name:= 'bob'. This way, we don't ever have to write "setters" either. Obviously the at: and at:put: reflection protocols will let us get around setting and getting if we need too (which we would, otherwise how would we get our compiledmethod back out of an object!)
So how does the sort of code look? Let's try some examples:
Smalltalk code: buildFeed | fQuery | self query value isEmpty ifTrue: [^Dialog warn: (UserMessage defaultString: 'No Query specified!' key: #bfNoQuery)]. fQuery := self getBlogPulseQuery. Cursor wait showWhile: [self feedViewer addFeedsOrFeedFrom: fQuery]. self feedViewer checkNewAndAlertStates. self accept value: true New language code: buildFeed | fQuery | query value isEmpty ifTrue: [^Dialog warn: (UserMessage defaultString: 'No Query specified!' key: #bfNoQuery)]. fQuery := getBlogPulseQuery. Cursor wait showWhile: [feedViewer addFeedsOrFeedFrom: fQuery]. feedViewer checkNewAndAlertStates. accept value: true
A lot of the 'self' receivers disappear because we can talk about ourself more fluently now. This makes it very clear when we're talking about something not related to us.
Some unique things that happen when you start to break the language down like this. An instance of an object isn't sacred to the class that created it. It inherits it's behaviour from "A place" which is just a value in a slot - you can change this whenever you want. You can turn a Person in to an Organisation without any intense cost. Or for a more realistic example, you can change the security policy set on an object - which is inheriting security behaviour - by giving it a different security object in its inheriting security slot.
I'd say this is the start of what I'm looking for in a language - I have a lot more to say on the matter, so I hope to write some more posts on this new mythical language when I get the time.
By Sean on June 3, 2005, 11:22:03 am
By Rich Demers on June 3, 2005, 12:14:04 pm
By Brian Rice on June 3, 2005, 2:22:46 pm
By Michael Lucas-Smith on June 3, 2005, 7:53:18 pm
By Michael Lucas-Smith on June 3, 2005, 11:44:44 pm
By Michael Lucas-Smith on June 4, 2005, 12:40:54 am
By Peter William Lount on June 4, 2005, 6:31:52 pm