Hi,
I'm getting deeply interested in mathematical expression macros. Codes of Lisp is one big chunk of macros that forms a parse tree. I think our flexible Smalltalk can mimic Lisp by expression object. Below is a source code of Negation class, capable of negating objects by sending #negated method to its subject object. By redifining #doesNotUnderstand: method, this object can get along with other non-expression objects as well. ---- class definition ---- Object subclass: #Negation instanceVariableNames: 'object' classVariableNames: '' poolDictionaries: '' classInstanceVariableNames: '' ---- object side implementation ----- value ^self object negated doesNotUnderstand: failedMessage ^failedMessage forwardTo: self value ---- class side implementation ----- of: anObject ^self new object: anObject ---- Example ---- Negation of: 3 "creating instance" (Negation of: 3) value "evaluating the value" Negation of: (Negation of: 3)) "nesting parse tree" (Negation of: 3) + 10 "Getting along with other objects" ---- The End ---- Currently I've succeeded in more of a kind like Addition Subtraction, Multiplication, Dvision, Sine, Cosine, Tangent classes. They can form a big expression tree. expression := Sine of: (Multiplication left: (Subtraction left: 7 right (Negation of: 3)) right: 1/3) ). "It is quickly resembling Lisp as it grows bigger. :-) " expression value. Expression tree of objects seems like decorator pattern applied. The question is ... - Can this approach reach the level of Lisp's powerful abstraction? - Can this be used elsewhere than mathematical domain? - Is there simpler way to do the same? Have a good one Howard |
Hi Howard,
> Hi, > > I'm getting deeply interested in mathematical expression macros. Codes > of Lisp is one big chunk of macros that forms a parse tree. > > I think our flexible Smalltalk can mimic Lisp by expression object. > Below is a source code of Negation class, capable of negating objects > by sending #negated method to its subject object. S-Mock does a similar thing for expressing constraints on method calls, I implemented Less,Greater,Equal,Unique etc. (they are all in the Constraints package). http://www.macta.f2s.com/Thoughts/smock.html Tim |
In reply to this post by Howard Oh
Howard
> expression := Sine of: (Multiplication left: (Subtraction left: 7 right > (Negation of: 3)) right: 1/3) ). "It is quickly resembling Lisp as it > grows bigger. :-) " > expression value. > > Expression tree of objects seems like decorator pattern applied. > > The question is ... > - Can this approach reach the level of Lisp's powerful abstraction? Probably not directly -- you will also need an idea of a variable (name) which can be bound for the evaluation of a particular expression (and which can be rebound in sub-expressions). Without that you don't have a way to express functions -- functions, and especially higher-order functions, are where Lisp (and other) languages get most of their generality. BTW, there have been several Lisp-in-Smalltalk implementations; you might find it interesting to take a look at them -- if only for comparison with your own approach. The only one I can remember off-hand is the one which forms one part of JUN ("JUN for Smalltalk" -- a VW framework). > - Can this be used elsewhere than mathematical domain? Certainly. Why not ? Numbers are only objects, after all ;-) > - Is there simpler way to do the same? You might like to try adding Constant and Variable to the framework. So you can say: Add left: (Constant value: 1) right: (Variable name: #x). If you then add convenience methods: Number>>asExpr (or even Object>>asExpr) which answers a Constant, and Symbol>>asExpr which answers a Variable, then you can shorten that to: Add left: (1 asExpr) right (#x asExpr). (Brackets added only for clarity here.) Then you can implement #+ on your expression objects, to answer + anObject ^ Add left: self: right: anObject asExpr. and so the original expression can be shortened to: (1 asExpr) + (#x asExpr). or even, 1 asExpr + #x. You could generalise that quite a lot by defining lots of different combination methods. Alternatively you could use DNU processing to look up the selector in a Symbol->Class table (or maybe a Symbol->Block table), and use that to create new expression objects implicitly. Lastly, you might want to be able to evaluate an expression in an environment: expr := #x asExpr + 1 + #y. env := (LookupTable new) at: #x put: 200; at: #y put: 21; yourself. expr valueWithEnv: env, --> 222 All just ideas, of course, I'm not trying to suggest that there is a single "right" way to do this (or that the above is even /a/ right way). Nor trying to tell you how to have fun with this stuff -- you are doing that already ;-) -- chris |
In reply to this post by Tim M
> S-Mock does a similar thing for expressing constraints on method calls, I
> implemented Less,Greater,Equal,Unique etc. (they are all in the Constraints > package). > > http://www.macta.f2s.com/Thoughts/smock.html > > Tim wow really? I'd better look around. Best Regards |
In reply to this post by Chris Uppal-3
Chris Uppal wrote:
> Probably not directly -- you will also need an idea of a variable (name) which > can be bound for the evaluation of a particular expression (and which can be > rebound in sub-expressions). Without that you don't have a way to express > functions -- functions, and especially higher-order functions, are where Lisp > (and other) languages get most of their generality. How did you know that I need Variable(s). I added Variable class to my image. ---- class defninition ---- Object subclass: #Variable instanceVariableNames: 'name value' classVariableNames: '' poolDictionaries: '' classInstanceVariableNames: '' Variable>>partialDifferentiation: aVariable ^self = aVariable ifTrue: [ 1 ] ifFalse: [ 0 ] Addition>>partialDifferentiation: aVariable ^self class left: (leftOperand partialDifferentiation: aVariable) right: (rightOperand partialDifferentiation: aVariable) It can now generate partial differentiation of an expression object. x := Variable new. x name: 'x'. x value: 100. x partialDifferentiation: x. f := Addition left: x right: 23. f value. "123" (f partialDifferentiation: x) value. "an Addition object #printOn:ed 1+0 " ----------------------- As you can see the definition of the Variable class, it keeps it name and value in it, which is different from your example ; "var & env" model. I think your design better because expression and variable values are separated, therefore it can be passed over to other contexts and use different variable values there. But... I didn't do it that way. If i do that my #value will be pointless(should be deleted maybe), and #valueWithEnv: requires one parameter. Using keyword message in place of unary message means lots a lot of parenthesis required to do the same thing. (Hey! It really is becoming like Lisp!) Maybe i can be lucky enough to get hints from SmalltalkWorkspace or BlockClosure. Best Regard. Howard |
Free forum by Nabble | Edit this page |