unom
unom

Reputation: 11476

Can the inside of a block be aware of the selector it received and evaluate itself based on that?

Presume we have a block in the code somewhere and we assign it to a variable (either instance or local) like this.

someName := [ anInstanceVariable doThis. anotherInstanceVariable doThat.] 

From the outside I would like to use it this way:

someName someMessageTheBlockDoesntImplement: argument.

Would it be possible for the block to act on the specific selector someName and have anInstanceVariable or anotherInstanceVariable perform it and return those objects respectively?

PS. It would act as a kind of forwarder of sorts.

Upvotes: 2

Views: 96

Answers (3)

Göran Krampe
Göran Krampe

Reputation: 79

The question is fairly confusing. First i guess you meant "the selector someMessageTheBlockDoesntImplement:" and not "someName". But what you are basically asking for is being able to make forwarding proxies.

You normally do not use BlockClosures to do that, instead you create a class MyForwarder which inherits either Object or even ProtoObject (depending on which Smalltalk you use and how transparent you want the forwarder to be).

Add your two instance variables in this class so that you can hold them and some method so you can set them etc.

Then you implement #doesNotUnderstand: in MyForward which looks something like:

doesNotUnderstand: aMessage
    aMessage selector == #thisMessageGoesToFirstGuy
        ifTrue: [
             ^ firstGuy perform: aMessage selector withArguments: aMessage arguments ] 
        ifFalse: [
             ^ secondGuy perform: aMessage selector withArguments: aMessage arguments ] 

Code not tested but you get the idea.

Upvotes: 4

Bob Nemec
Bob Nemec

Reputation: 511

All Smalltalk dialects allow some way to access the current context. In VisualWorks and Pharo, you use #thisContext. So if you have a method like...

Object>>someMethod
    ^[thisContext sender inspect] value

...send evaluate: 'abc' someMethod ... you'll get aMethodContext --> ByteString(Object)>>someMethod

or...

Object>>someMethod: aBlock
    ^aBlock value

'abc' someMethod: [thisContext sender inspect]

--> ByteString(Object)>>someMethod:

If you need to see more of the stack, you can use something like...

| context frames | 
context := thisContext.
frames := OrderedCollection new.
[context isNil or: [context stack isNil]] whileFalse: [
    frames add: context.
    context := context sender].
^frames

In VA Smalltalk, you could write...

someMethod
    ^[Processor activeProcess currentFrame context inspect] value

...a BlockContextTemplate --> [] in Object>>#someMethod

Looking at debugger code is a good way to see what other techniques you could use.

Upvotes: 6

Uko
Uko

Reputation: 13396

You can always implement doesNotUnderstand: of the BlockClosure class in a way:

doesNotUnderstand: aMessage
    ^ self value: aMessage selector value: aMessage arguments  

and then you have to have a block like this:

[ :selector :args |
    ^ { anInstanceVariable      perform: selector withArguments: args.
        anotherInstanceVariable perform: selector withArguments: args } ]

But why do you want to do something like that?

Upvotes: 3

Related Questions