> In the system I'm working on (brilliantly inspired and sublimely implemented
> in Smalltalk) I have various calculations which are the result of several
> sub-calculations. I'd like to have a class that doesn't resolve the
> expressions, but instead just keeps adding them.
...
> Does something like this already exist?
In VisualWorks 5i.4, I made a quick hack based on the Smalltalk compiler classes.
I've appended the fileout for it.
To use it, just send the message #symbolic to the first value in the calculation.
Sending the message #! creates a comment attached to the preceding literal.
For example, your first case would look like:
0.15 symbolic * 100 / (365 !'days in year') * 60
Printing this expression results in
0.15 * 100 / (365 !'days in year') * 60 "2.46575"
You may also send it the message #explain to see the structure and intermediate results. The result is
' 0.15
*
100
"= 15.0"
/
365 !''days in year''
"= 0.0410959"
*
60
"= 2.46575"
'
Here's the fileout (hope it gets through OK)
Cheers,
Hans-Martin
--------
<?xml version="1.0"?>
<st-source>
<time-stamp>From VisualWorks?, Release 5i.4 of August 9, 2001 on October 18, 2001 at 2:23:40 am</time-stamp>
<!-- Package SymbolicValues= -->
<class>
<name>SymbolicConstant</name>
<environment>Smalltalk</environment>
<super>Kernel.LiteralNode</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>SymbolicMath</category>
<attributes>
<package>SymbolicValues</package>
</attributes>
</class>
<class>
<name>SymbolicMessage</name>
<environment>Smalltalk</environment>
<super>Kernel.SimpleMessageNode</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>SymbolicMath</category>
<attributes>
<package>SymbolicValues</package>
</attributes>
</class>
<methods>
<class-id>Core.Object</class-id> <category>symbolic</category>
<body package="SymbolicValues">! aString
^ self symbolic ! aString</body>
<body package="SymbolicValues">symbolic
^SymbolicConstant new value: self</body>
</methods>
<methods>
<class-id>Kernel.ProgramNode</class-id> <category>symbolic</category>
<body package="SymbolicValues">doesNotUnderstand: aMessage
self isSymbolicValue ifFalse: [^super doesNotUnderstand: aMessage].
^SymbolicMessage new
receiver: self
selector: aMessage selector
arguments: (aMessage arguments collect: [:each | each symbolic])</body>
<body package="SymbolicValues">isSymbolicValue
^false</body>
<body package="SymbolicValues">symbolic
^self</body>
<body package="SymbolicValues">inspectorActions
"The inspector sends this message and relies on error handling to detect which objects don't implement it, instead of implementing it like this in Object..."
^#()</body>
<body package="SymbolicValues">explain
| str |
str := WriteStream on: String new.
self printExplainedOn: str indent: 0.
^str contents</body>
<body package="SymbolicValues">! aString
comment := aString</body>
</methods>
<methods>
<class-id>SymbolicConstant</class-id> <category>testing</category>
<body package="SymbolicValues">isSymbolicValue
^true</body>
</methods>
<methods>
<class-id>SymbolicConstant</class-id> <category>accessing</category>
<body package="SymbolicValues">precedence
^3</body>
</methods>
<methods>
<class-id>SymbolicConstant</class-id> <category>printing</category>
<body package="SymbolicValues">printOn: aStream
self printOn: aStream indent: 0 precedence: 2</body>
<body package="SymbolicValues">printOn: aStream indent: indent precedence: precedence
comment == nil ifFalse: [
precedence > 1 ifFalse: [aStream nextPut: $(]].
super printOn: aStream indent: indent precedence: precedence.
comment == nil ifFalse: [
aStream nextPutAll: ' !'; print: comment.
precedence > 1 ifFalse: [aStream nextPut: $)]]</body>
<body package="SymbolicValues">printExplainedOn: aStream indent: indent
aStream tab: indent.
self printOn: aStream.
aStream cr</body>
</methods>
<methods>
<class-id>SymbolicMessage</class-id> <category>testing</category>
<body package="SymbolicValues">isSymbolicValue
^true</body>
</methods>
<methods>
<class-id>SymbolicMessage</class-id> <category>evaluating</category>
<body package="SymbolicValues">value
^receiver value
perform: selector
withArguments: (arguments collect: [:arg | arg value])</body>
</methods>
<methods>
<class-id>SymbolicMessage</class-id> <category>printing</category>
<body package="SymbolicValues">addIndentForReceiver: aSymbolicValue
^(aSymbolicValue precedence > self precedence or: [aSymbolicValue precedence = 3])
ifTrue: [1]
ifFalse: [0]</body>
<body package="SymbolicValues">printExplainedOn: aStream indent: indent
receiver printExplainedOn: aStream indent: indent + (self addIndentForReceiver: receiver).
selector keywords with: arguments do: [:kw :arg |
aStream tab: indent; nextPutAll: kw; cr.
arg printExplainedOn: aStream indent: indent+1].
aStream tab: indent; nextPutAll: '"= '; print: self value; nextPut: $"; cr</body>
<body package="SymbolicValues">printOn: aStream
self printOn: aStream indent: 0.
aStream nextPutAll: ' "'; print: self value; nextPut: $"</body>
</methods>
</st-source>