Smalltalk delegation / storing message selectors - selector

I am learning Squeak and I was wondering if there is a way to 'store' a message to be sent to an object.
For example I would like to be able to do the following :
Delegator>>performWith: arg
|target method|
target := MyObject new.
method := #myMethod. "Not sure what goes here"
target sendMessage: method withArgs: arg. "Or how this call would work"
An alternative approach would be to specify both the target and the method in a block. However it is possible to do using the above approach?

Well, perhaps i misunderstood your question, but you nearly guessed the answer:
Send this message to your "target":
perform: aSymbol with: anObject
or:
perform: aSymbol withArguments: anArrayOfArguments
In your example:
target perform: method with: arg

You can also try using an instance of the MessageSend object.
msg := MessageSend receiver: target selector: #myMethod arguments: #(arg1 arg2).
msg value. "sends the message to it's receiver"
MessageSend can be used as is. Squeak, Pharo, etc. use MessageSend as the base class for MorphicAlarm - which one can use to delay the execution of message until a certain time in the future.
Happy coding!

Related

The use of ">>" in Pharo/Smalltalk

I am implementing futures in Pharo. I came across this website http://onsmalltalk.com/smalltalk-concurrency-playing-with-futures. I am following this example and trying to replicate it on Pharo. However, I get to this point the last step and I have no idea what ">>" means: This symbol is not also included as part of Smalltalk syntax in http://rigaux.org/language-study/syntax-across-languages-per-language/Smalltalk.html.
BlockClosure>>future
^ SFuture new value: self fixTemps
I can see future is not a variable or a method implemented by BlockClosure. What should I do with this part of the code to make the promises/futures work as indicated at http://onsmalltalk.com/smalltalk-concurrency-playing-with-futures? I cannot add it on the Playground or as a method to my Promise class as it is, or am I missing something?
After adding the future method to BlockClosure, this is the code I try on the PlayGround.
value1 := [200 timesRepeat:[Transcript show: '.']. 6] future.
value2 := [200 timesRepeat:[Transcript show: '+']. 6] future.
Transcript show: 'other work'.
Transcript show: (value1 + value2).
Date today
The transcript displays the below error instead of the expected value of 12.
UndefinedObject>>DoIt (value1 is Undeclared)
UndefinedObject>>DoIt (value2 is Undeclared)
For some reason that it would be nice to learn, there is a traditional notation in Smalltalk to refer to the method with selector, say, m in class C which is C>>m. For example, BlockClosure>>future denotes the method of BlockClosure with selector #future. Interestingly enough, the expression is not an evaluable Smalltalk one, meaning, it is not a Smalltalk expression. It is just a succinct way of saying, "what comes below is the source code of method m in class C". Just that.
In Smalltalk, however, methods are objects too. In fact, they are instances of CompiledMethod. This means that they can be retrieved by sending a message. In this case, the message is methodAt:. The receiver of the message is the class which implements the method and the argument is the selector (respectively, C and #m, or BlockClosure and #future in your example).
Most dialects, therefore, implement a synonym of methodAt: named >>. This is easily done in this way:
>> aSymbol
^self methodAt: aSymbol
This puts the Smalltalk syntax much closer to the traditional notation because now BlockClosure>>future looks like the expression that would send the message >> to BlockClosure with argument future. However, future is not a Symbol unless we prepend it with #, namely #future. So, if we prefix the selector with the # sign, we get the literal Symbol #future, which is a valid Smalltalk object. Now the expression
BlockClosure >> #future
becomes a message, and its result after evaluating it, the CompiledMethod with selector #future in the class BlockClosure.
In sum, BlockClosure>>future is a notation, not a valid Smalltalk expression. However, by tweaking it to be BlockClosure >> #future, it becomes an evaluable expression of the language that returns the method the notation referred to.

What is the difference between self and yourself in Smalltalk?

In Smalltalk, there are two terms often found within a method body: self and yourself.
What is the difference between them?
The reserved word self is a pseudo variable (you cannot assign to it) that refers to the current receiver of the method where it is used. On the other side yourself is a message you can send to any object to get that very same object.
The implementation of yourself is
yourself
^self
meaning that the message yourself will behave as I just explained.
The reason why yourself exists is to support message cascading, where you put it as the last message to make sure the resulting expression will answer with the receiver:
^receiver
msg1;
msg2;
yourself
If msg2 might answer with something different from the receiver you can append the yourself message to ignore that answer and return receiver instead. Of course you could have achieved the same result by writing:
receiver
msg1;
msg2.
^receiver
Because of the simplicity of these two examples, it might be hard to understand what the advantage would be. However, consider that receiver is not a variable but a complex expression, something like.
^(self msg: arg1 arg: arg2)
msg1;
msg2;
yourself.
Without using yourself you would have to add a temporary to save the value of the receiver to achieve the same:
| answer |
answer := self msg: arg1 arg: arg2.
answer
msg1;
msg2.
^answer
which is a little bit more verbose.
To summarize, self is a reserved word that refers to the current receiver and yourself is just a regular method that is there just for convenience.
self is a synonym for an object: specifically the receiver of the message that invoked the method. It is used within the body of a method.
yourself is a message that you can send to an object, that returns the receiver of the message.
anObject yourself returns anObject.
yourself is often used at the end of a message cascade within a method body.
When you want the return value from the method to be the receiver, but the final message in the cascade returns something else, you could write either:
self aMessageReturningTheReceiver;
aMessageReturningTheArgument: anArgument .
^self
or
self aMessageReturningTheReceiver;
aMessageReturningTheArgument: anArgument;
yourself

Implementation of new with argument in smalltalk

I basically want to implement new which can accept argument e.x obj := SomeClass new: 'a'. I tried to implement this way
initialize: bdata
data := bdata
But this doesn't work. seems like I am doing some silly mistake because of lack of knowledge. I try to google it but couldn't find any example. Please help.
In Smalltalk, new and new: are not keywords, but regular messages. They are simply implemented by the object's class. To write a method for an objects's class (rather than for an instance), click the "class" button in the system browser. There, you could implement your new: method.
Note, however, that it is usually not a good idea to name your own instance creation method new:. Since this is a regular method, you can name it anything you want. For example, MyClass withBData: foo. Make it a nice descriptive name. It could look like
withBData: bdata
| inst |
inst := self new.
inst bdata: bdata.
^inst
Your code is too short to tell what is wrong. In general you should have an initialize with arg, something like this:
initialize: arg
self initialize.
instVar := arg
Then you can implement new: like this:
new: arg
^ self basicNew
initialize: arg;
yourself
Note that new is implemented as self basicNew initialize, so if you are calling initialize from your custom initialization method, you shouldn't use new in your custom new, use basicNew instead
You can use the basicNew method if you need to use your argument in the initialize method (as Uko mentions in his answer above).
withBData: bdata
^ (self basicNew bdata: bdata) initialize

smalltalk method call with parameters

I wrote the following method in smalltalk:
initializeWithStart: startWith step: theStep count: theCount
self initialize.
startNumber := startWith.
stepSize := theStep.
countUntil := theCount.
and i just want to call this method after creating an object from the workspace. so I wrote:
mySq := ArithmeticsS new.
mySq initializeWithStart: '2' step:'4' count:'10'.
why do I get error "MessageNotUnderstood:undefinedobject>>initializeWithStart:step:count:"?
We can't say for sure without more context, but it looks like you created the new method on the class side, instead of the instance side.
In the workspace, you chose to send the message to an instance.
To find out, evaluate (print)
ArithmeticsS respondsTo: #initializeWithStart:step:count:
If the method is on the class side, that will be true. Delete that method and save it on the instance side instead.
Now evaluate
ArithmeticsS new respondsTo: #initializeWithStart:step:count:
With new, this checks for your method on the instance side. It should be true. My guess from the information you posted is that it is false, which means that you didn't save the method in the right place.
Check the documentation for your dialect of Smalltalk to confirm how to save an instance method.
You should instantiate like this:
mySq := ArithmeticsS new initializeWithStart: 2 step:4 count:10.

How do I change a method's name dynamically in squeak?

I have a class and I want to change the name of a specific method in run time.
I guess there's a method in the 'Behavior' class that does it. But I just can't find it. any help? [in squeak]
The normal way a user does this is to modify the method source and 'accept it' then delete the old version. So it's not likely that basic Squeak includes a single method to do this, although I could be wrong.
However if you install, for example, OmniBrowser there is a method refactoring called 'rename' and you could inspect and find code to perform this refactoring. It is fairly complex, firstly because the refactorings are done using the command pattern which involves a little redirection to work out, but secondly because this is a fairly complex refactoring which includes modifying the call sites.
What you are suggesting puts HUGE red flags up for me.
What is it you are trying to accomplish with this?
Do you mean you want to change the name of the method you are calling at runtime?
If so, that's easy.
do something like:
|methodName|
methodName := self useMethod1 ifTrue: [#method1 ] ifFalse:[ #method2 ].
self perform: methodName.
You best use a refactoring
r := RenameMethodRefactoring
renameMethod: #foo:foo:
in: Foo
to: #bar:bar:
permutation: (1 to: #foo:foo: numArgs).
r execute.
Avoid voodoo magic in real code when possible.
That being said you can do some very interesting things by manipulating methods dynamically.
For instance the code bricks in Etoys are translated into Smalltalk methods. Other DSL implementations can also benefit from similar metaprogramming tricks.
After experimenting a bit I came up with the following code for renaming unary methods:
renameMethod: oldMethod inClass: class to: newMethod
| oldSelector newSelector source parser |
oldSelector := oldMethod asSymbol.
newSelector := newMethod asSymbol.
oldSelector = newSelector ifTrue: [^self].
"Get method category"
category := (LocatedMethod location: class selector: oldSelector) category.
"Get method source code"
source := class sourceCodeAt: oldSelector.
"Replace selector in method source"
(parser := class parserClass new) parseSelector: source.
source := (newSelector asString), (source allButFirst: parser endOfLastToken).
"Compile modified source"
class compile: source classified: category.
"Remove old selector"
class removeSelector: oldSelector
You could probably find an easier way to do this if you browse through the Squeak code a bit longer than I did.
You can't change a method's name, really, because it doesn't have one.
An object's method dictionary maps Symbols to CompiledMethods. "Change the name of a method" means "move the CompiledMethod value from this key to that key".