|
|
Class: Block
Object
|
+--ExecutableFunction
|
+--CompiledCode
|
+--Block
|
+--CheapBlock
|
+--VarArgBlock
- Package:
- stx:libbasic
- Category:
- Kernel-Methods
- Version:
- rev:
1.176
date: 2010/04/23 13:33:01
- user: mb
- file: Block.st directory: libbasic
- module: stx stc-classLibrary: libbasic
- Author:
- Claus Gittinger
Blocks are pieces of executable code which can be evaluated by sending
them a value-message (''value'', ''value:'', ''value:value:'' etc).
In smalltalk, Blocks provide the basic (and heavily used) mechanism
for looping, enumerating collection elements, visitors, exception
handling, unwinding, delayed execution and processes.
Blocks are never created explicitely; the only creation
is done by the compilers, when some sourceCode is compiled to either
machine or byteCode.
Blocks with arguments need a message of type ''value:arg1 ... value:argn''
for evaluation; the number of arguments passed when evaluating must match
the number of arguments the block was declared with otherwise an error is
raised. Blocks without args need a ''value'' message for evaluation.
Blocks keep a reference to the context where the block was declared -
this allows blocks to access the methods arguments and/or variables.
This is still true after the method has returned - since the
block keeps this reference, the methods context will NOT die in this case.
(i.e. Blocks are closures in Smalltalk/X)
A return (via ^-statement) out of a block will force a return from the
blocks method context (if it is still living) - this make the implementation
of long-jumps and control structures possible.
(If the method is not alive (i.e. has already returned), a return out of the
block will trigger an error)
Long-jump is done by defining a catchBlock as ''[^ self]''
somewhere up in the calling-tree. Then, to do the long-jump from out of some
deeply nested method, simply do: ''catchBlock value''.
[Instance variables:]
home <Context> the context where this block was created (i.e. defined)
this may be a blockContext or a methodContext
nargs <SmallInteger> the number of arguments the block expects
sourcePos <SmallInteger> the character position of its source, in chars
relative to methods source beginning
initialPC <SmallInteger> the start position within the byteCode
for compiled blocks, this is nil.
[Class variables:]
InvalidNewSignal raised if a Block is tried to be created
with new (which is not allowed).
Only the VM is allowed to create Blocks.
NOTICE: layout known by runtime system and compiler - do not change
Process
Context
Collection
[contexts. blocks & unwinding]
initialization
-
initialize
-
create signals raised by various errors
instance creation
-
byteCode: bCode numArgs: numArgs numStack: nStack sourcePosition: sourcePos initialPC: initialPC literals: literals
-
create a new cheap (homeless) block.
Not for public use - this is a special hook for the compiler.
-
byteCode: bCode numArgs: numArgs numVars: numVars numStack: nStack sourcePosition: sourcePos initialPC: initialPC literals: literals
-
create a new cheap (homeless) block.
Not for public use - this is a special hook for the compiler.
-
new
-
catch creation of blocks - only the system creates blocks.
If you really need a block (assuming, you are some compiler),
use basicNew and setup the instance carefully
-
new: size
-
catch creation of blocks - only the system creates blocks
queries
-
isBuiltInClass
-
return true if this class is known by the run-time-system.
Here, true is returned for myself, false for subclasses.
Compatibility-ANSI
-
argumentCount
-
VisualAge/ANSI compatibility: alias for #numArgs.
return the number of arguments I expect for evaluation
-
ensure: aBlock
-
evaluate the receiver and return its result.
After evaluation, also evaluate aBlock but ignore its result.
aBlock is also evaluated in case of abnormal termination.
(the same as #valueNowOrOnUnwindDo:)
-
ifCurtailed: aBlock
-
evaluate the receiver - when some method sent within unwinds (i.e. does
a long return), evaluate the argument, aBlock.
This is used to make certain that cleanup actions (for example closing files etc.) are
executed regardless of error actions.
This is the same as #valueOnUnwindDo:
Compatibility-Dolphin
-
deferredValue
-
Compatibility method - do not use in new code.
Dolphin's alias for futureValue
Compatibility-Squeak
-
ifError: handlerBlock
-
same as onError: - for squeak compatibility.
Notice, that the handlerBlock may take 0,1 or 2 args.
(1 arg -> the exception;
2 args -> the errorString and the erronous receiver)
-
valueWithPossibleArgs: argArray
-
Compatibility-V'Age
-
valueOnReturnDo: aBlock
-
VisualAge compatibility: alias for #ifCurtailed:
evaluate the receiver - when some method sent within unwinds
(i.e. does a long return), evaluate the argument, aBlock.
This is used to make certain that cleanup actions
(for example closing files etc.) are executed regardless of error actions.
Q: is this the exact semantics of V'Ages method ?
the documentation is unclean; it could be a valueNowOrOnUnwindDo: ...
Javascript support
-
typeof
-
return a string describing what I am
accessing
-
home
-
return the receivers home context (the context where it was
created). For cheap blocks, nil is returned
-
homeMethod
-
return the receivers home method.
Thats the method where the block was created.
-
method
-
return the receivers method
(the method where the block was created).
Obsolete: use #homeMethod for ST80 compatibility.
** This is an obsolete interface - do not use it (it may vanish in future versions) **
-
methodHome
-
return the receivers method home context (the context where it was
defined). For cheap blocks, nil is returned
-
numArgs
-
return the number of arguments I expect for evaluation.
Please use argumentCount, which is ANSI
binary storage
-
storeBinaryDefinitionOn: stream manager: manager
-
compatibility-Cola & Pepsi
-
arity
-
conversion
-
asVarArgBlock
-
convert myself into a varArg block;
this one has 1 formal argument, which gets the list
of actual arguments when evaluated.
-
beCurryingBlock
-
make myself a currying block;
thats a block which, if invoked with less-than-expected arguments,
returns another block which provides the provided argument(s) and expects the remaining args.
Read any book on functional programming, if you dont understand this.
-
beVarArg
-
convert myself into a varArg block;
this one has 1 formal argument, which gets the list
of actual arguments when evaluated.
copying
-
deepCopyUsing: aDictionary postCopySelector: postCopySelector
-
raise an error - deepCopy is not allowed for blocks
error handling
-
invalidCodeObject
-
this error is triggered by the interpreter when a non-Block object
is about to be executed.
In this case, the VM sends this to the bad method (the receiver).
Can only happen when the Compiler/runtime system is broken or
someone played around.
evaluation
-
value
-
evaluate the receiver with no block args.
The receiver must be a block without arguments.
-
value: arg
-
evaluate the receiver with one argument.
The receiver must be a 1-arg block.
-
value: arg1 optionalArgument: arg2
-
evaluate the receiver.
Optionally pass up one or to two arguments (if the receiver is a one/two arg block).
-
value: arg1 optionalArgument: arg2 and: arg3
-
evaluate the receiver.
Optionally pass up one, two or three arguments (if the receiver is a 1/2/3-arg block).
-
value: arg1 value: arg2
-
evaluate the receiver with two arguments.
The receiver must be a 2-arg block.
-
value: arg1 value: arg2 optionalArgument: arg3
-
evaluate the receiver.
Optionally pass two or threearguments (if the receiver is a 2/3-arg block).
-
value: arg1 value: arg2 value: arg3
-
evaluate the receiver with three arguments.
The receiver must be a 3-arg block.
-
value: arg1 value: arg2 value: arg3 value: arg4
-
evaluate the receiver with four arguments.
The receiver must be a 4-arg block.
-
value: arg1 value: arg2 value: arg3 value: arg4 value: arg5
-
evaluate the receiver with five arguments.
The receiver must be a 5-arg block.
-
value: arg1 value: arg2 value: arg3 value: arg4 value: arg5 value: arg6
-
evaluate the receiver with six arguments.
The receiver must be a 6-arg block.
-
value: arg1 value: arg2 value: arg3 value: arg4 value: arg5 value: arg6 value: arg7
-
evaluate the receiver with seven arguments.
The receiver must be a 7-arg block.
-
value: arg1 value: arg2 value: arg3 value: arg4 value: arg5 value: arg6 value: arg7 value: arg8
-
evaluate the receiver with eight arguments.
The receiver must be a 8-arg block.
-
value: arg1 value: arg2 value: arg3 value: arg4 value: arg5 value: arg6 value: arg7 value: arg8 value: arg9
-
evaluate the receiver with 9 arguments.
The receiver must be a 9-arg block.
-
value: arg1 value: arg2 value: arg3 value: arg4 value: arg5 value: arg6 value: arg7 value: arg8 value: arg9 value: arg10
-
evaluate the receiver with 10 arguments.
The receiver must be a 10-arg block.
-
value: arg1 value: arg2 value: arg3 value: arg4 value: arg5 value: arg6 value: arg7 value: arg8 value: arg9 value: arg10 value: arg11
-
evaluate the receiver with 11 arguments.
The receiver must be a 11-arg block.
-
value: arg1 value: arg2 value: arg3 value: arg4 value: arg5 value: arg6 value: arg7 value: arg8 value: arg9 value: arg10 value: arg11 value: arg12
-
evaluate the receiver with 12 arguments.
The receiver must be a 12-arg block.
-
valueAt: priority
-
evaluate the receiver, at the given prioriy;
i.e. change the priority for the execution of the receiver.
Bad name: should be called evaluateWithPriority: or similar
-
valueWithArguments: argArray
-
evaluate the receiver with arguments taken from argArray.
ArgArray must be either an Array or nil.
The size of the argArray must match the number of arguments the receiver expects.
-
valueWithOptionalArgument: arg
-
evaluate the receiver.
Optionally pass an argument (if the receiver is a one arg block).
-
valueWithOptionalArgument: arg1 and: arg2
-
evaluate the receiver.
Optionally pass up to two arguments (if the receiver is a one/two arg block).
-
valueWithOptionalArgument: arg1 and: arg2 and: arg3
-
evaluate the receiver.
Optionally pass up to three arguments (if the receiver is a one/two/three arg block).
-
valueWithOptionalArgument: arg1 and: arg2 and: arg3 and: arg4
-
evaluate the receiver.
Optionally pass up to four arguments (if the receiver is a one/two/three/four arg block).
-
valueWithOptionalArguments: argArray
-
evaluate the receiver with arguments as required taken from argArray.
Only the required number of arguments is taken from argArray or nil;
(i.e. argArray may be larger than the required number).
If the size of the argArray is smaller than the number of arguments, an error is raised.
-
valueWithPossibleArguments: argArray
-
evaluate the receiver with arguments as required taken from argArray.
If argArray provides less than the required number of arguments,
nil is assumed for any remaining argument.
(i.e. argArray may be smaller than the required number).
Only the required number of arguments is taken from argArray or nil;
(i.e. argArray may be larger than the required number).
exception handling
-
on: aSignalOrSignalSetOrException do: exceptionBlock
-
added for ANSI compatibility; evaluate the receiver,
handling aSignalOrSignalSetOrException.
The 2nd argument, exceptionBlock is evaluated
if the signal is raised during evaluation.
-
on: aSignalOrSignalSetOrException do: exceptionBlock ensure: ensureBlock
-
added for ANSI compatibility; evaluate the receiver,
handling aSignalOrSignalSetOrException.
The 2nd argument, exceptionBlock is evaluated
if the signal is raised during evaluation.
The 3rd argument, ensureBlock is evaluated in any case - even if the activity
was unwound due to an unhandled exception.
-
on: anExceptionHandler do: exceptionBlock on: anExceptionHandler2 do: anExceptionBlock2
-
added for ANSI compatibility; evaluate the receiver,
handling aSignalOrSignalSetOrException.
The 2nd argument, exceptionBlock is evaluated
if the signal is raised during evaluation.
-
valueWithExceptionHandler: handler
-
evaluate myself. If any of the signals in handler is raised,
evaluate the corresponding handler block.
-
valueWithWatchDog: exceptionBlock afterMilliseconds: aTimeLimit
-
a watchdog on a block's execution. If the block does not finish its
evaluation after aTimeLimit milliseconds, it is interrupted (aborted) and
exceptionBlock's value is returned. The receiver's code must be prepared
for premature returning (by adding ensure blocks, as required)
exception handling private
-
exceptionHandlerFor: anException in: aContext
-
answer the exceptionHandler (the Error or signal) for anException from aContext.
-
handlerForSignal: exceptionHandler context: theContext originator: originator
-
answer the handler block for the exceptionHandler from originator.
The handler block is retrieved from aContext.
Answer nil if the exceptionHandler is not handled.
-
handlerProtectedBlock: doBlock inContext: context
-
set the block that is protected by an exception handler in context.
This is the receiver of the #on:do: or #valueWithExceptionHandler:.
Needed for #restartDo:
looping
-
doUntil: aBlock
-
repeat the receiver block until aBlock evaluates to true.
The receiver is evaluated at least once.
This is the same as '... doWhile:[... not]'
-
doWhile: aBlock
-
repeat the receiver block until aBlock evaluates to false.
The receiver is evaluated at least once.
-
loop
-
repeat the receiver forever
(the receiver block should contain a return somewhere).
The implementation below was inspired by a corresponding Self method.
-
loopWithExit
-
the receiver must be a block of one argument. It is evaluated in a loop forever,
and is passed a block, which, if sent a value:-message, will exit the receiver block,
returning the parameter of the value:-message. Used for loops with exit in the middle.
Inspired by a corresponding Self method.
-
repeat
-
repeat the receiver forever - same as loop, for ST-80 compatibility.
(the receiver block should contain a return somewhere).
-
valueWithExit
-
the receiver must be a block of one argument. It is evaluated, and is passed a block,
which, if sent a value:-message, will exit the receiver block, returning the parameter of the
value:-message. Used for premature returns to the caller.
Taken from a manchester goody (a similar construct also appears in Self).
-
valueWithRestart
-
-
whileFalse
-
evaluate the receiver while it evaluates to false (ST80 compatibility)
-
whileFalse: aBlock
-
evaluate the argument, aBlock while the receiver evaluates to false.
- usually open coded by compilers, but needed here for #perform
and expression evaluation.
-
whileTrue
-
evaluate the receiver while it evaluates to true (ST80 compatibility)
-
whileTrue: aBlock
-
evaluate the argument, aBlock while the receiver evaluates to true.
- usually open coded by compilers, but needed here for #perform
and expression evaluation.
parallel evaluation
-
futureValue
-
Fork a synchronised evaluation of myself.
Starts the evaluation in parallel now, but synchronizes
any access to wait until the result is computed.
-
futureValue: aValue
-
Fork a synchronised evaluation of myself.
Starts the evaluation in parallel now, but synchronizes
any access to wait until the result is computed.
-
futureValue: aValue value: anotherValue
-
Fork a synchronised evaluation of myself.
Starts the evaluation in parallel now, but synchronizes
any access to wait until the result is computed.
-
futureValue: aValue value: anotherValue value: bValue
-
Fork a synchronised evaluation of myself.
Starts the evaluation in parallel now, but synchronizes
any access to wait until the result is computed.
-
futureValueWithArguments: anArray
-
Fork a synchronised evaluation of myself.
Starts the evaluation in parallel now, but synchronizes
any access to wait until the result is computed.
-
lazyValue
-
Fork a synchronised evaluation of myself. Only starts
the evaluation when the result is requested.
-
lazyValue: aValue
-
Fork a synchronised evaluation of myself. Only starts
the evaluation when the result is requested.
-
lazyValue: aValue value: anotherValue
-
Fork a synchronised evaluation of myself. Only starts
the evaluation when the result is requested.
-
lazyValue: aValue value: anotherValue value: bValue
-
Fork a synchronised evaluation of myself. Only starts
the evaluation when the result is requested.
-
lazyValueWithArguments: anArray
-
Fork a synchronised evaluation of myself. Only starts
the evaluation when the result is requested.
printing & storing
-
printBlockBracketsOn: aStream
-
-
printOn: aStream
-
append a a printed representation of the block to aStream
-
storeOn: aStream
-
private-accessing
-
byteCode: bCode numArgs: numArgs numVars: numVars numStack: numStack sourcePosition: srcPos initialPC: iPC literals: lits
-
set all relevant internals.
DANGER ALERT: this interface is strictly private.
-
initialPC
-
return the initial pc for evaluation.
-
initialPC: initial
-
set the initial pc for evaluation.
DANGER ALERT: this interface is for the compiler only.
-
numArgs: numArgs
-
set the number of arguments the receiver expects for evaluation.
DANGER ALERT: this interface is for the compiler only.
-
setHome: aContext
-
-
source
-
-
source: aString
-
set the source - only to be used, if the block is not contained in a method.
This interface is for knowledgable users only.
-
sourcePosition: position
-
set the position of the source within my method.
This interface is for the compiler only.
privileged evaluation
-
valueUninterruptably
-
evaluate the receiver with interrupts blocked.
This does not prevent preemption by a higher priority processes
if any becomes runnable due to the evaluation of the receiver
(i.e. if a semaphore is signalled).
-
valueUnpreemptively
-
evaluate the receiver without the possiblity of preemption
(i.e. at a very high priority)
process creation
-
fork
-
create a new process executing the receiver at the current priority.
-
forkAt: priority
-
create a new process executing the receiver at a different priority.
-
forkNamed: aString
-
create a new process, give it a name and let it start
executing the receiver at the current priority.
-
forkWith: argArray
-
create a new process executing the receiver,
passing elements in argArray as arguments to the receiver block.
-
newProcess
-
create a new (unscheduled) process executing the receiver
-
newProcessWithArguments: argArray
-
create a new (unscheduled) process executing the receiver,
passing the elements in argArray as arguments to the receiver block.
-
promise
-
create a promise on the receiver. The promise will evaluate the
receiver and promise to return the value with the #value message.
The evaluation will be performed as a separate process.
Asking the promise for its value will either block the asking process
(if the evaluation has not yet been finished) or return the value
immediately.
-
promiseAt: prio
-
create a promise on the receiver. The promise will evaluate the
receiver and promise to return the value with the #value message.
The evaluation will be performed as a separate process running at prio.
Asking the promise for its value will either block the asking process
(if the evaluation has not yet been finished) or return the value
immediately.
testing
-
isBlock
-
return true, if this is a block - yes I am
-
isVarArgBlock
-
return true, if this block accepts a variable number of arguments
unwinding
-
unwindHandlerInContext: aContext
-
given a context which has been marked for unwind,
retrieve the handler block.
This avoids hardwiring access to the first argument in
#unwind methods (and theoretically allows for other unwinding
methods to be added)
unwinding-old
-
value: arg onUnwindDo: aBlock
-
evaluate the receiver, passing it one argument
- when some method sent within unwinds (i.e. does
a long return), evaluate the argument, aBlock.
This is used to make certain that cleanup actions (for example closing files etc.) are
executed regardless of error actions
-
valueNowOrOnUnwindDo: aBlock
-
evaluate the receiver - after that, or when some method sent within unwinds (i.e. does
a long return), evaluate the argument, aBlock.
This is used to make certain that cleanup actions (for example closing files etc.) are
executed regardless of error actions.
Same as the more modern #ensure:
-
valueOnUnwindDo: aBlock
-
evaluate the receiver - when some method sent within unwinds (i.e. does
a long return), evaluate the argument, aBlock.
This is used to make certain that cleanup actions (for example closing files etc.) are
executed regardless of error actions
Same as the more modern #ifCurtailed:
visiting
-
acceptVisitor: aVisitor with: aParameter
-
define a block and evaluate it:
|b|
b := [ Transcript showCR:'hello' ].
Transcript showCR:'now evaluating the block ...'.
b value.
|
even here, blocks are involved:
(although, the compiler optimizes things if possible)
Transcript showCR:'now evaluating one of two blocks ...'.
1 > 4 ifTrue:[
Transcript showCR:'foo'
] ifFalse:[
Transcript showCR:'bar'
]
|
here things become obvious:
|yesBlock noBlock|
yesBlock := [ Transcript showCR:'foo' ].
noBlock := [ Transcript showCR:'bar' ].
Transcript showCR:'now evaluating one of two blocks ...'.
1 > 4 ifTrue:yesBlock
ifFalse:noBlock
|
simple loops:
not very objectOriented:
|i|
i := 1.
[i < 10] whileTrue:[
Transcript showCR:i.
i := i + 1
]
|
using integer protocol:
1 to:10 do:[:i |
Transcript showCR:i.
]
|
interval protocol:
(1 to:10) do:[:i |
Transcript showCR:i.
]
|
looping over collections:
bad code:
(only works with numeric-indexable collections)
|i coll|
coll := #(9 8 7 6 5).
i := 1.
[i <= coll size] whileTrue:[
Transcript showCR:(coll at:i).
i := i + 1.
]
|
just as bad (well, marginally better ;-):
(only works with numeric-indexable collections)
|coll|
coll := #(9 8 7 6 5).
1 to:coll size do:[:i |
Transcript showCR:(coll at:i).
]
|
the smalltalk way:
(works with any collection)
|coll|
coll := #(9 8 7 6 5).
coll do:[:element |
Transcript showCR:element.
]
|
Rule: use enumeration protocol of the collection instead of
manually indexing it. [with few exceptions]
processes:
forking a lightweight process (thread):
[
Transcript showCR:'waiting ...'.
Delay waitForSeconds:2.
Transcript showCR:'here I am'.
] fork
|
some with low prio:
[
Transcript showCR:'computing ...'.
10000 factorial.
Transcript showCR:'here I am'.
] forkAt:(Processor userBackgroundPriority)
|
handling exceptions:
Object errorSignal handle:[:ex |
Transcript showCR:'exception handler forces return'.
ex return
] do:[
Transcript showCR:'now, doing something bad ...'.
1 / 0.
Transcript showCR:'not reached'
]
|
performing cleanup actions:
Object errorSignal handle:[:ex |
Transcript showCR:'exception handler forces return'.
ex return
] do:[
[
Transcript showCR:'doing something bad ...'.
1 / 0.
Transcript showCR:'not reached'
] valueOnUnwindDo:[
Transcript showCR:'cleanup'
]
]
|
delayed execution (visitor pattern):
(looking carefully into the example,
C/C++ programmers may raise their eyes ;-)
|showBlock countBlock
howMany
top panel b1 b2|
howMany := 0.
showBlock := [ Transcript showCR:howMany ].
countBlock := [ howMany := howMany + 1 ].
top := StandardSystemView extent:200@200.
panel := HorizontalPanelView origin:0.0@0.0 corner:1.0@1.0 in:top.
b1 := Button label:'count up' in:panel.
b1 action:countBlock.
b2 := Button label:'show value' in:panel.
b2 action:showBlock.
top open.
Transcript showCR:'new process started;'.
Transcript showCR:'notice: the blocks can still access the'.
Transcript showCR:' howMany local variable.'.
|
|