Expression object?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Expression object?

Howard Oh
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


Reply | Threaded
Open this post in threaded view
|

Re: Expression object?

Tim M
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


Reply | Threaded
Open this post in threaded view
|

Re: Expression object?

Chris Uppal-3
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


Reply | Threaded
Open this post in threaded view
|

Re: Expression object?

Howard Oh
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


Reply | Threaded
Open this post in threaded view
|

Re: Expression object?

Howard Oh
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