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
Related
I am using ByteBuddy to generate a class.
Prior to working with DynamicType.Builder, I was going to store a MethodCall as an instance variable:
private final MethodCall frobCall =
MethodCall.invoke(ElementMatchers.named("frob")); // here I invoke a method I'm going to define as part of the instrumented type
Then later in my generation logic for the instrumented type I define the frob method to do something:
.defineMethod("frob")
.intercept(...etc....) // here I define frob to do something
…and I define the (let's say) baz method to invoke frob:
.defineMethod("baz")
.withParameter(...) // etc.
.intercept(frobCall); // invokes "frob", which I've just defined above
(I am trying to keep this simple and may have mistyped something but I hope you can see the gist of what I'm trying to do.)
When I make() my DynamicType, I receive an error that indicates that the dynamic type does not define frob. This is mystifying to me, because of course I have defined it, as you can see above.
Is there some restriction I am unaware of that prohibits ElementMatchers from identifying instrumented type methods that are defined later? Do I really have to use MethodDescription.Latent here?
It should match all methods of the instrumented type. If this is not happening as expected, please set a breakpoint in MethodCall.MethodLocator.ForElementMatcher to see why the method is not showing up. I assume it is filtered by your method matcher.
I noticed however that it did not include private methods which is now fixed and will be released within Byte Buddy 1.10.18.
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.
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!
instance creation method, like
ClassName new
To aid with some details,
we could write a = arithmetic method in abstract class,
then doubledispatch them in subclasses.
could we use that in instance creation?
I have tried new but it fail. Leads to some predefined basic new method.
Double Dispatch doesn't really make sense in the new case. The idea behind double dispatch, is that you can't determine the correct behavior by dispatching only to the receiver. The type of the (single) argument has an equal effect on which behavior is chosen (dispatched). In other words, double dispatch only makes sense if you have arguments to your methods, new being unary, it does not.
That said, you can certainly implement your own new method that overrides the stock default inherited one. And you can make it do all kinds of interesting things. It's common to do some sort of environment check to determine what subclass is appropriate.
AbstractClass>>>new
^self platform = #unix
ifTrue: [SubclassThatLeveragesUnix basicNew]
ifFalse: [CrappyPCSubclass basicNew]
Note that we use basicNew here, rather than new. If you used new you would need to either implement distinct overrides in those subclasses, otherwise it would just inherit and resend the AbstractClass>>>new message again.
... or you could do something like:
AbstractClass class>>#new
^ (self platform concreteClassFor: self) basicNew initialize.
which is basically same idea, but without ifs :)
The key point of double dispatch is that by swapping the receiver and the argument of the primary message, you call a second time a virtual call and that you get then the effect of selecting a method based on the message receiver and its argument. Therefore you need to have message with argument.
Here is a typical example of double dispatch: addition integer and floats and performing adequate conversion.
Integer>>+ arg
^ arg sumFromInteger: self
Float>>+ arg
^ arg sumFromFloat: self
Integer>>sumFromInteger: anInt
<primitive adding to ints>
Integer>>sumFromFloat: aFloat
^ self asFloat + aFloat
Float>>sumFromFloat: aFloat
<primitive adding two floats>
Float>>sumFromInteger: anInt
^ self + anInt asFloat
Now 1 + 1.0 will hit first + on Integer then sumFromInt: then + then sumFromFloat. Note that we have enough information so we could shortcut the second + invocation,
What the example shows is that during the first call, the dynamic message resolution finds on method (so it defining like a dynamic case) and then by swapping the argument and the receiver, the dynamic message resolution performs another case based on the arg. So at the end you get a method selected using the two objects of the original call.
Now about your question: In Pharo class messages are dynamically looked up so you could implement instance creation methods using double dispatch without problem but the goal is unclear.
MyClass class>>newWith: arg
arg newFromMyClass: aClass
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".