[comp.lang.smalltalk] Back to the future

budd@orstcs.cs.ORST.EDU (08/22/87)

The following is a short article that will appear in the next edition of the
Little Smalltalk newsletter; however I thought it might be of more general
intrest since it is not specific to Little Smalltalk.  The code is given in
Little Smalltalk form, however it can be easily translated into other
dialects.  The rest of the newletter should be ready about the middle of
september.
--tim budd
----------------------------------------------------
     I was recently reading about a nifty new  idea  in  BBN
Butterfly  Lisp,  a  dialect  of Scheme.  The butterfly is a
multiprocessor machine, and the new feature is one primitive
they are experimenting with to provide the user with easy to
use multiprocessing primitives.  I will simplify a bit here,
but  basically the idea is to provide a new statement called
``future''.  When the user evaluates an expression such as

        (future expression)

What they get back is a box.  At some future  date  the  box
will  contain  the value of the expression.  The computation
of the expression then takes place in parallel with the con-
tinuted  execution  of  the  program.  If at some later time
they try to read the contents of the box, if the  result  of
the execution has not yet been completed the running process
is suspended until the expression is  evaluated.   Once  the
box  has  been filled, however, it can be read and reread as
often as desired.

     In BBN Lisp this primitive is  implemented  using  con-
tinuations, something which is absent from Smalltalk.  Never
the less, a similar idea can be easily  produced.   We  will
create a new class, called Future.  Instances of Future will
respond to two message; eval:, which  takes  as  argument  a
block and starts the task of evaluating it, and value, which
returns the value of the  expression.   Here  is  the  class
description for future:

        Class Future
        | result sem |
        [
            eval: aBlock
                sem <- Semaphore new: 0.
                [ result <- aBlock value. sem signal ] fork
        |
            value
                sem wait.
                sem signal.
                ^ result
        ]


     Here is one test case I tried using future:


        Class Test
        [
            test    | sum i x |
                sum <- 0.
                'about to create the future' print.
                x <- Future new eval:
                    [ (1 to: 100) do: [:i | sum <- sum + 1].
                       'finished future' print. sum ].
                'created future' print.
                x value print.
                'done with future' print
        ]


     And here is a the output from this test case:

        %st -i future.st
        Little Smalltalk
            Test new test
        about to create the future
        created future
        finished future
        100
        done with future
        Test


     Notice how the program continued execution, printing  a
message  before  execution  of the ``future'' expression had
completed.

miw@mucs.UX.CS.MAN.AC.UK (Mario Wolczko) (08/24/87)

I too noticed some time ago (after reading Halstead's paper on MultiLisp,
in ACM Trans. on Prog. Langs., 7:4, Oct 1985, 501-538) that futures were a
nice way of using parallelism.  Anyway, I hacked a simple version of
Future, very similar to Tim Budd's, except that the "value" message to
retrieve the value from the future was not required.  I used
doesNotUnderstand: to trap all incoming messages and resynchronise the two
processes.  (This is an identical device to that described by Geoffrey
Pascoe in the OOPSLA paper, "Encapsulators: a New Software Paradigm in
Smalltalk-80", pp.341-346.)

Building on this, Trevor Hopkins wrote lots and lots of code that used
Futures to show what they could do, and also built lazy evaluators too
using similar tricks.  Below is some code that, as they say, I happen to
have prepared earlier.

Caveats: the future trick doesn't work on primitives that don't send
messages to get internal state of argument objects.  For example, primitive
equivalence (==) compares the oops of the receiver and argument directly.
If the argument is a Future, it will use its oop rather than the oop of the
result of the Future.  Similar problems are encountered on arithmetic
primitives. 

Anyway, this may provide some entertainment for those of you who like to
build your own control structures.

Share and enjoy,

Mario Wolczko
------------------------------------------------------------------------
Dept. of Computer Science	ARPA:	miw%ux.cs.man.ac.uk
The University			USENET: mcvax!ukc!man.cs.ux!miw
Manchester M13 9PL		JANET:	miw@uk.ac.man.cs.ux
U.K.				+44-61-273 7121 x 5699
	"Manchester---the home of Virtual Memory"
------------------------------------------------------------------------

'From Smalltalk-80, version 2, of April 1, 1983 on 29 March 1987 at 5:13:27 pm'!

!Object methodsFor: 'parallel evaluation'!

touch
	"Simply returns self.  If the receiver is an uncompleted
	 Future or Lazy, this forces complete evaluation."

	^self! !

'From Smalltalk-80, version 2, of April 1, 1983 on 29 March 1987 at 5:03:50 pm'!

!Array methodsFor: 'products'!

dotProduct: anArray
	"Answers with the sum of the products of each element
	 of the receiver with anArray.  Creates an error if the
	 receiver and anArray are different sizes."

	(self size = anArray size) ifFalse: [^self error: 'arrays must be the same size'].
	^self fastDotProduct: anArray!

fastDotProduct: anArray 
	"Answers with the sum of the products of each element 
	 of the receiver with anArray."

	| sum |
	sum _ 0.
	1 to: self size do: [:index |
		sum _ sum + ((self at: index) * (anArray at: index))].
	^sum! !

'From Smalltalk-80, version 2, of April 1, 1983 on 29 March 1987 at 5:08:06 pm'!

Object subclass: #Future
	instanceVariableNames: 'result semaphore '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Kernel-Processes'!

Future comment:
'I represent an execution in progress.  Any messages sent to me are delayed until execution has completed.'!

!Future methodsFor: 'synchronising'!

doesNotUnderstand: aMessage
	"Any message to a Future will end up here."

	semaphore wait.	"Wait for evaluation to complete"
						"(if not already completed)"
	semaphore signal.	"Wake up anything else that might be waiting"
	^result perform: aMessage selector withArguments: aMessage arguments! !

!Future methodsFor: 'evaluating'!

block: aBlock
	"Execute aBlock in parallel with whatever called me, but
	 ensure that any messages sent to me before execution
	 of the block has terminated are suspended until it has terminated."

	semaphore _ Semaphore new.
	[result _ aBlock value.  semaphore signal] fork!

block: aBlock value: aValue
	"Execute aBlock in parallel with whatever called me, but
	 ensure that any messages sent to me before execution
	 of the block has terminated are suspended until it has terminated."

	semaphore _ Semaphore new.
	[result _ aBlock value: aValue.  semaphore signal] fork!

block: aBlock value: value1 value: value2
	"Execute aBlock in parallel with whatever called me, but
	 ensure that any messages sent to me before execution
	 of the block has terminated are suspended until it has terminated."

	semaphore _ Semaphore new.
	[result _ aBlock value: value1 value: value2.
	 semaphore signal] fork!

block: aBlock value: value1 value: value2 value: value3
	"Execute aBlock in parallel with whatever called me, but
	 ensure that any messages sent to me before execution
	 of the block has terminated are suspended until it has terminated."

	semaphore _ Semaphore new.
	[result _ aBlock value: value1 value: value2 value: value3.
	 semaphore signal] fork!

block: aBlock valueWithArguments: anArray
	"Execute aBlock in parallel with whatever called me, but
	 ensure that any messages sent to me before execution
	 of the block has terminated are suspended until it has terminated."

	semaphore _ Semaphore new.
	[result _ aBlock valueWithArguments: anArray.
	 semaphore signal] fork! !

"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

Future class
	instanceVariableNames: ''!

!Future class methodsFor: 'examples'!

example1
	"Starts evaluating the factorial immediately, but waits until
	 the result is available before printing the answer!!"

	| fac |
	fac _ [100 factorial] futureValue.
	Transcript show: 'evaluating factorial...'.
	Transcript show: fac printString

	"Future example1"!

example2
	"An example illustrating the use of multiple futures and
	 explicit resynchronisation."

	"Starts evaluating both factorials immediately, but waits until
	 both blocks have finished before continuing."

	| fac1 fac2 |
	fac1 _ [Transcript show: 'Starting fac1.. '. 100 factorial] futureValue.
	fac2 _ [Transcript show: 'Starting fac2.. '. 120 factorial] futureValue.
	fac2 touch.
	fac1 touch.
	Transcript show: 'both completed.'.

	"Future example2"!

example3
	"Example showing how arguments may be passed to futures."

	| temp |
	temp _ [:x :y | 10 * x * y] futureValue: 3 value: 4.
	Transcript cr; show: temp printString.

	"Future example3"! !

!Future class methodsFor: 'class initialization'!

initialize
 	"must avoid the checks"

	superclass _ nil

	"Future initialize."! !

Future initialize!

'From Smalltalk-80, version 2, of April 1, 1983 on 29 March 1987 at 5:09:10 pm'!

Object subclass: #Lazy
	instanceVariableNames: 'result startSemaphore endSemaphore '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Kernel-Processes'!

Lazy comment:
'I represent an execution which may not be required.  I will
not start execution until at least one message has been
received.  The messages sent to me are delayed until execution
has completed.'!

!Lazy methodsFor: 'synchronising'!

doesNotUnderstand: aMessage
	"Any message to a Lazy will end up here."

	startSemaphore signal.		"Start the evaluation."
	endSemaphore wait.		"Wait until evaluation completed."
	endSemaphore signal.		"Wake up anything else."
	^result perform: aMessage selector withArguments: aMessage arguments
		"Perform the message, having re-synchronised."! !

!Lazy methodsFor: 'evaluating'!

block: aBlock
	"Execute aBlock in parallel, but ensure that any messages sent
	 to me before execution of the block has terminated are
	 suspended until it has terminated. Do not start the evaluation
	 until at least one message has been sent to the receiver."

	startSemaphore _ Semaphore new.
	endSemaphore _ Semaphore new.
	[startSemaphore wait.
	 result _ aBlock value.
	 endSemaphore signal] fork!

block: aBlock value: aValue
	"Execute aBlock in parallel, but ensure that any messages sent
	 to me before execution of the block has terminated are
	 suspended until it has terminated. Do not start the evaluation
	 until at least one message has been sent to the receiver."

	startSemaphore _ Semaphore new.
	endSemaphore _ Semaphore new.
	[startSemaphore wait.
	 result _ aBlock value: aValue.
	 endSemaphore signal] fork!

block: aBlock value: value1 value: value2
	"Execute aBlock in parallel, but ensure that any messages sent
	 to me before execution of the block has terminated are
	 suspended until it has terminated. Do not start the evaluation
	 until at least one message has been sent to the receiver."

	startSemaphore _ Semaphore new.
	endSemaphore _ Semaphore new.
	[startSemaphore wait.
	 result _ aBlock value: value1 value: value2.
	 endSemaphore signal] fork!

block: aBlock value: value1 value: value2 value: value3
	"Execute aBlock in parallel, but ensure that any messages sent
	 to me before execution of the block has terminated are
	 suspended until it has terminated. Do not start the evaluation
	 until at least one message has been sent to the receiver."

	startSemaphore _ Semaphore new.
	endSemaphore _ Semaphore new.
	[startSemaphore wait.
	 result _ aBlock value: value1 value: value2 value: value3.
	 endSemaphore signal] fork!

block: aBlock valueWithArguments: anArray
	"Execute aBlock in parallel, but ensure that any messages sent
	 to me before execution of the block has terminated are
	 suspended until it has terminated. Do not start the evaluation
	 until at least one message has been sent to the receiver."

	startSemaphore _ Semaphore new.
	endSemaphore _ Semaphore new.
	[startSemaphore wait.
	 result _ aBlock valueWithArguments: anArray.
	 endSemaphore signal] fork! !

"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

Lazy class
	instanceVariableNames: ''!

!Lazy class methodsFor: 'class initialization'!

initialize
 	"must avoid the checks"

	superclass _ nil

	"Lazy initialize."! !

!Lazy class methodsFor: 'examples'!

example1
	"Evaluates the factorial, starting only when the
	 result is actually required (when printString is sent)."

	| fac |
	fac _ [100 factorial] futureValue.
	Transcript show: 'Doing nothing. '.
	(Delay forSeconds: 2) wait.
	Transcript show: fac printString

	"Lazy example1"!

example2
	"Starts evaluating both factorials only when required (by the touch),
	 and waits until both blocks have finished before continuing."

	| fac1 fac2 |
	fac1 _ [Transcript show: 'Starting fac1.. '. 100 factorial] lazyValue.
	fac2 _ [Transcript show: 'Starting fac2.. '. 120 factorial] lazyValue.
	fac2 touch.
	fac1 touch.
	Transcript show: 'both completed.'.

	"Lazy example2"!

example3
	"Demonstrates how to pass arguments to a lazy evaluation block."

	| temp |
	temp _ [:x :y :z | x * y * z] lazyValueWithArguments: #(2 3 4).
	Transcript cr; show: temp printString.

	"Lazy example3"! !

Lazy initialize!

'From Smalltalk-80, version 2, of April 1, 1983 on 29 March 1987 at 5:14:17 pm'!

Object subclass: #ParallelEvaluation
	instanceVariableNames: 'futures '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Kernel-Processes'!

ParallelEvaluation comment:
'I represent a collection of explicitly parallel computations.  All of the computations are completed before I return.
'!

!ParallelEvaluation methodsFor: 'private'!

initialize
	"Create a new OrderedCollection of futures."

	futures _ OrderedCollection new.! !

!ParallelEvaluation methodsFor: 'synchronisation'!

do
	"Evaluates all futures in parallel, forcing resynchronisation."

	futures isEmpty ifTrue: [^nil].
	futures do: [:each | each touch]! !

!ParallelEvaluation methodsFor: 'adding'!

add: aBlock
	"Add aBlock to the collection of futures to be evaluated in parallel."

	futures addLast: (Future new block: aBlock)!

add: aBlock value: aValue
	"Add aBlock to the collection of futures to be evaluated in parallel."

	futures addLast: (Future new block: aBlock value: aValue)!

add: aBlock value: aValue value: anotherValue
	"Add aBlock to the collection of futures to be evaluated in parallel."

	futures addLast: (Future new block: aBlock value: aValue value: anotherValue)!

add: aBlock value: aValue value: anotherValue value: bValue
	"Add aBlock to the collection of futures to be evaluated in parallel."

	futures addLast:
	 (Future new block: aBlock value: aValue value: anotherValue value: bValue)!

add: aBlock withArguments: anArray
	"Add aBlock to the collection of futures to be evaluated in parallel."

	futures addLast: (Future new block: aBlock valueWithArguments: anArray)! !

"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

ParallelEvaluation class
	instanceVariableNames: ''!

!ParallelEvaluation class methodsFor: 'instance creation'!

new

	^super new initialize! !

!ParallelEvaluation class methodsFor: 'examples'!

example1
	"An example of using a parallel evaluation. "

	| t |
	t _ ParallelEvaluation new.
	t add: [Transcript cr; show: 'First block. '].
	t add: [(Delay forSeconds: 5) wait. Transcript cr; show: 'Second block. '].
	t add: [Transcript cr; show: 'Third block. '].
	t add: [(Delay forSeconds: 2) wait. Transcript cr; show: 'Forth block. '].
	t add: [Transcript cr; show: 'Fifth block. '].
	t do.
	Transcript cr; show: 'All blocks finished. '.

	"ParallelEvaluation example1."!

example2
	"Uses the BlockContext method."

	[(Delay forSeconds: 2) wait. Transcript cr; show: 'First Block. ']
		inParallelWith: [Transcript cr; show: 'Second Block. '].
	Transcript cr; show: 'Both blocks finished.'

	"ParallelEvaluation example2."!

example3
	"Also uses the BlockContext method."

	[Transcript cr; show: 'First Block. ']
		inParallelWith: [Transcript cr; show: 'Second Block. ']
		with: [Transcript cr; show: 'Third Block. '].
	Transcript cr; show: 'All completed. '

	"ParallelEvaluation example3."!

example4
	"Example showing how to pass arguments to blocks
	 which are to be evaluated in parallel."

	[:x | Transcript cr; show: x printString] value: 10
		inParallelWith: [:y | Transcript cr; show: (2 * y) printString] value: 3.

	"ParallelEvaluation example4."!

sortExample
	"Uses a parallel in-place Quicksort algorithm to sort collections.
	 Note that on a single processor, this is somewhat slower
	 than the single-process version!!"

	Transcript show: (
	#(5 4 4 3 2 2 1 6 5 6 8 7 6 6 4 5 6 7 6 5 4 3
	   2 1 2 3 4 5 6 7 8 9 0 9 8 7 6 5 4 3 2 1 2 3
	   4 5 6 7 8 9 0 9 8 7 6 5 4 3 2 1 1 2 4 6 6 6)
			asParallelSortedCollection) printString.

	"ParallelEvaluation sortExample."!

sortExample2
	"Uses a parallel in-place Quicksort algorithm to sort dictionaries.
	 Note that on a single processor, this is somewhat slower
	 than the single-process version!!"

	Transcript cr; show: (
		Time millisecondsToRun: [Smalltalk asSortedCollection]) printString.
	Transcript cr; show: (
		Time millisecondsToRun: [Smalltalk asParallelSortedCollection]) printString.

	"ParallelEvaluation sortExample2."! !

'From Smalltalk-80, version 2, of April 1, 1983 on 29 March 1987 at 5:04:08 pm'!

!BlockContext methodsFor: 'parallel evaluation'!

futureValue
	"Fork a synchronised evaluation of myself. Starts the
	 evaluation in parallel immediately."

	^Future new block: self!

futureValue: aValue
	"Fork a synchronised evaluation of myself. Starts the
	 evaluation in parallel immediately."

	^Future new block: self value: aValue!

futureValue: aValue value: anotherValue
	"Fork a synchronised evaluation of myself. Starts the
	 evaluation in parallel immediately."

	^Future new block: self value: aValue value: anotherValue!

futureValue: aValue value: anotherValue value: bValue
	"Fork a synchronised evaluation of myself. Starts the
	 evaluation in parallel immediately."

	^Future new block: self value: aValue value: anotherValue value: bValue!

futureValueWithArguments: anArray
	"Fork a synchronised evaluation of myself. Starts the
	 evaluation in parallel immediately."

	^Future new block: self valueWithArguments: anArray!

inParallelWith: aBlock
	"Executes the receiver in parallel with aBlock. Continue
	 only after both have completed."

	| aParallelEvaluation |
	aParallelEvaluation _ ParallelEvaluation new add: self.
	aParallelEvaluation add: aBlock.
	aParallelEvaluation do!

inParallelWith: aBlock value: aValue
	"Executes the receiver in parallel with aBlock. Continue
	 only after both have completed."

	| aParallelEvaluation |
	aParallelEvaluation _ ParallelEvaluation new add: self.
	aParallelEvaluation add: aBlock value: aValue.
	aParallelEvaluation do!

inParallelWith: aBlock with: anotherBlock
	"Executes the receiver in parallel with aBlock and anotherBlock.
	 Continue only after all have completed."

	| aParallelEvaluation |
	aParallelEvaluation _ ParallelEvaluation new add: self.
	aParallelEvaluation add: aBlock.
	aParallelEvaluation add: anotherBlock.
	aParallelEvaluation do!

lazyValue
	"Fork a synchronised evaluation of myself. Only starts
	 the evaluation when the result is requested."

	^Lazy new block: self!

lazyValue: aValue
	"Fork a synchronised evaluation of myself. Only starts
	 the evaluation when the result is requested."

	^Lazy new block: self value: aValue!

lazyValue: aValue value: anotherValue
	"Fork a synchronised evaluation of myself. Only starts
	 the evaluation when the result is requested."

	^Lazy new block: self value: aValue value: anotherValue!

lazyValue: aValue value: anotherValue value: bValue
	"Fork a synchronised evaluation of myself. Only starts
	 the evaluation when the result is requested."

	^Lazy new block: self value: aValue value: anotherValue value: bValue!

lazyValueWithArguments: anArray
	"Fork a synchronised evaluation of myself. Only starts
	 the evaluation when the result is requested."

	^Lazy new block: self valueWithArguments: anArray!

parallelAnd: aBlock 
	"Executes the receiver in parallel with aBlock. Once both   
	 have completed, perform a logical AND operation."

	| first second |
	first _ self futureValue.
	second _ aBlock futureValue.
	^first touch & second touch!

parallelEqv: aBlock 
	"Executes the receiver in parallel with aBlock. Once both   
	 have completed, perform a logical equivalence (exclusive-NOR)
	 operation."

	| first second |
	first _ self futureValue.
	second _ aBlock futureValue.
	^first touch eqv: second touch!

parallelOr: aBlock 
	"Executes the receiver in parallel with aBlock. Once both   
	 have completed, perform a logical OR operation."

	| first second |
	first _ self futureValue.
	second _ aBlock futureValue.
	^first touch | second touch!

parallelPerform: aSymbol with: aBlock 
	"Executes the receiver in parallel with aBlock. Once both  
	 have completed, perform the operation given by aSymbol."

	| first second |
	first _ self futureValue.
	second _ aBlock futureValue.
	^first touch perform: aSymbol with: second touch!

parallelXor: aBlock 
	"Executes the receiver in parallel with aBlock. Once both   
	 have completed, perform a logical equivalence (exclusive-NOR)
	 operation."

	| first second |
	first _ self futureValue.
	second _ aBlock futureValue.
	^first touch xor: second touch!

value: aValue inParallelWith: aBlock
	"Executes the receiver in parallel with aBlock. Continue
	 only after both have completed."

	| aParallelEvaluation |
	aParallelEvaluation _ ParallelEvaluation new add: self value: aValue.
	aParallelEvaluation add: aBlock.
	aParallelEvaluation do!

value: aValue inParallelWith: aBlock value: anotherValue
	"Executes the receiver in parallel with aBlock. Continue
	 only after both have completed."

	| aParallelEvaluation |
	aParallelEvaluation _ ParallelEvaluation new add: self value: aValue.
	aParallelEvaluation add: aBlock value: anotherValue.
	aParallelEvaluation do! !

'From Smalltalk-80, version 2, of April 1, 1983 on 29 March 1987 at 5:19:49 pm'!

!SequenceableCollection methodsFor: 'parallel evaluation'!

parallelDo: aBlock 
	"Perform the block aBlock with all elements of the
	 receiver in parallel, using a parallel evaluation mechanism."

	"Watch out for blocks with side-effects, as the blocks may
	 be executed in any order."

 	| index length aPE |
	aPE _ ParallelEvaluation new.
	index _ 0.
	length _ self size.
	[(index _ index + 1) <= length]
		whileTrue: [aPE add: aBlock value: (self at: index)].
	aPE do		"ensure all blocks have terminated before continuing."! !

'From Smalltalk-80, version 2, of April 1, 1983 on 29 March 1987 at 5:20:34 pm'!

!SortedCollection methodsFor: 'adding'!

parallelAdd: newObject 
	"Don't actually force a sort, as we will always resort later."

	^super addLast: newObject! !

'From Smalltalk-80, version 2, of April 1, 1983 on 29 March 1987 at 5:20:36 pm'!

!SortedCollection methodsFor: 'adding'!

parallelAddAll: aCollection 
	"Use the parallel sorting mechanism, so
	 always insert everything and resort."

	aCollection do: [:each | super addLast: each].
	self parallelReSort! !

'From Smalltalk-80, version 2, of April 1, 1983 on 29 March 1987 at 5:20:30 pm'!

!SortedCollection methodsFor: 'private'!

parallelReSort
	self parallelSort: firstIndex to: lastIndex! !

'From Smalltalk-80, version 2, of April 1, 1983 on 29 March 1987 at 5:20:26 pm'!

!SortedCollection methodsFor: 'private'!

parallelSort: i to: j 
	"Sort elements i through j of self to be nondescending according to sortBlock.
	 Use the parallel evaluation mechanism to spawn off processes."

	| di dij dj tt ij k l n |
	"The prefix d means the data at that index."
	(n _ j + 1  - i) <= 1 ifTrue: [^self].	"Nothing to sort." 
	 "Sort di,dj."
	di _ self basicAt: i.
	dj _ self basicAt: j.
	(sortBlock value: di value: dj) "i.e., should di precede dj?"
		ifFalse: 
			[self swap: i with: j.
			 tt _ di.
			 di _ dj.
			 dj _ tt].
	n > 2
		ifTrue:  "More than two elements."
			[ij _ (i + j) // 2.  "ij is the midpoint of i and j."
			 dij _ self basicAt: ij.  "Sort di,dij,dj.  Make dij be their median."
			 (sortBlock value: di value: dij) "i.e. should di precede dij?"
			   ifTrue: 
				[(sortBlock value: dij value: dj) "i.e., should dij precede dj?"
				  ifFalse: 
					[self swap: j with: ij.
					 dij _ dj]]
			   ifFalse:  "i.e. di should come after dij"
				[self swap: i with: ij.
				 dij _ di].
			n > 3
			  ifTrue:  "More than three elements."
				["Find k>i and l<j such that dk,dij,dl are in reverse order.
				Swap k and l.  Repeat this procedure until k and l pass each other."
				 k _ i.
				 l _ j.
				 [[l _ l - 1.  k <= l and: [sortBlock value: dij value: (self basicAt: l)]]
				   whileTrue.  "i.e. while dl succeeds dij"
				  [k _ k + 1.  k <= l and: [sortBlock value: (self basicAt: k) value: dij]]
				   whileTrue.  "i.e. while dij succeeds dk"
				  k <= l]
				   whileTrue:
					[self swap: k with: l]. 
	"Now l<k (either 1 or 2 less), and di through dl are all less than or equal to dk
	through dj.  Sort those two segments in parallel."
				[self parallelSort: i to: l] inParallelWith: [self parallelSort: k to: j]]]! !

'From Smalltalk-80, version 2, of April 1, 1983 on 29 March 1987 at 5:07:32 pm'!

!Collection methodsFor: 'converting'!

asParallelSortedCollection
	"Answer a new instance of SortedCollection whose elements are the elements of
	the receiver.  The sort order is the default less than or equal ordering.
	 Use the parallel sorting mechanism."

	| aSortedCollection |
	aSortedCollection _ SortedCollection new: self size.
	aSortedCollection parallelAddAll: self.
	^aSortedCollection! !

'From Smalltalk-80, version 2, of April 1, 1983 on 29 March 1987 at 5:07:27 pm'!

!Collection methodsFor: 'converting'!

asParallelSortedCollection: aBlock 
	"Answer a new instance of SortedCollection whose elements are the elements of
	 the receiver.  The sort order is defined by the argument, aBlock.
	 Use the parallel sorting method."

	| aSortedCollection |
	aSortedCollection _ SortedCollection new: self size.
	aSortedCollection sortBlock: aBlock.
	aSortedCollection parallelAddAll: self.
	^aSortedCollection! !

'From Smalltalk-80, version 2, of April 1, 1983 on 29 March 1987 at 5:07:48 pm'!

!Dictionary methodsFor: 'converting'!

asParallelSortedCollection
	| aSortedCollection |
	aSortedCollection _ SortedCollection new: self size.
	self associationsDo: [:association | aSortedCollection parallelAdd: association].
	aSortedCollection parallelReSort.
	^aSortedCollection! !

'From Smalltalk-80, version 2, of April 1, 1983 on 29 March 1987 at 5:08:56 pm'!

!Integer methodsFor: 'factorization and divisibility'!

factorial3
	"Answer the factorial of the receiver.  For example,
	 6 factorial == 6*5*4*3*2*1. Signal an error if the receiver
	 is less than 0. Use explicitly parallel eager evaluation."

	self > 0
		ifTrue: [^self * [(self - 1) factorial] futureValue].
	self = 0
		ifTrue: [^1].
	self error: 'factorial invalid for: ' , self printString! !

'From Smalltalk-80, version 2, of April 1, 1983 on 29 March 1987 at 5:08:59 pm'!

!Integer methodsFor: 'factorization and divisibility'!

factorial4
	"Answer the factorial of the receiver.  For example,
	 6 factorial == 6*5*4*3*2*1. Signal an error if the receiver
	 is less than 0. Use explicitly parallel lazy evaluation."

	self > 0
		ifTrue: [^self * [(self - 1) factorial] lazyValue].
	self = 0
		ifTrue: [^1].
	self error: 'factorial invalid for: ' , self printString! !

Object subclass: #BinaryIntegration
	instanceVariableNames: 'function '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Parallel-algorithms'!

BinaryIntegration comment:
'I represent a numerical integration of a function.  I support a
parallel recursive sub-division algorithm, in both a throttled and
free form.
'!

!BinaryIntegration methodsFor: 'private'!

function: aBlock
	"Set the function to be integrated to aBlock."

	function _ aBlock! !

!BinaryIntegration methodsFor: 'accessing'!

areaBetween: left and: right 
	"Answer the calculated area between the two  
	 numbers."

	^self
		areaBetween: left
		and: right
		estimate: (self trapeziumBetween: left and: right)
		tolerance: 0.01!

areaBetween: left and: right estimate: anEstimate tolerance: aTolerance 
	"Answer the calculated area between the two  
	 numbers. Uses a parallel recursive sub-division algorithm."

	| mid areaLeft areaRight newEstimate |
	mid _ left + right / 2.
	areaLeft _ [self trapeziumBetween: left and: mid] futureValue.
	areaRight _ [self trapeziumBetween: mid and: right] futureValue.
	newEstimate _ (areaLeft touch) + (areaRight touch).
	(anEstimate - newEstimate) abs < aTolerance
		ifTrue: [^newEstimate]
		ifFalse: [
			^[self
				areaBetween: left
				and: mid
				estimate: areaLeft
				tolerance: aTolerance / 2]
			 parallelPerform: #+ with:
			  [self
					areaBetween: mid
					and: right
					estimate: areaRight
					tolerance: aTolerance / 2]
			]!

trapeziumBetween: left and: right 
	"Answer the area for a trapezium between the left 
	 and right indices."

	^(right - left) * ((self valueAt: left) + (self valueAt: right)) / 2!

valueAt: aNumber
	"Answers a number computed by the function associated with
	 the receiver at aNumber."

	^function value: aNumber! !

"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

BinaryIntegration class
	instanceVariableNames: ''!

!BinaryIntegration class methodsFor: 'instance creation'!

function: aBlock
	"Creates a new instance of a BinaryIntegration, with the
	 function given by aBlock."

	^super new function: aBlock! !

!BinaryIntegration class methodsFor: 'examples'!

example1
	"Illustrating the use of BinaryIntegration. Calculates the area under
	 the curve given by the function, using a parallel recursive sub-division
	 algorithm."

	| integration |
	integration _ BinaryIntegration function: [:x| (3*x*x*x) + (2*x*x) + 5].
	Transcript cr; show: (integration areaBetween: 0 and: 5) printString.

	"BinaryIntegration example1."

	"The correct answer is given by the following expression:"

	"Transcript cr; show: (((5 raisedTo: 4) * 3/4)
						 + ((5 raisedTo: 3) * 2/3)
						  + (5 * 5) asFloat) printString."!

example2
	"Illustrating the use of BinaryIntegration. Calculates the area under
	 the curve given by the function, using a parallel recursive sub-division
	 algorithm.  This version uses throttling to control the number of processes
	 created at a time."

	| integration |
	integration _ BinaryIntegration function: [:x| (3.0*x*x*x) + (2.0*x*x) + 5.0].
	Transcript cr; show: (integration throttledAreaBetween: 0 and: 5) printString.

	"BinaryIntegration example2."! !

Object subclass: #TwoDimMatrix
	instanceVariableNames: 'matrix '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Parallel-algorithms'!

TwoDimMatrix comment:
'I represent the class of two-dimensional arrays (matrices).  My
internal representation is an array of arrays.  I support various
operations in both serial and explicitly parallel form.
'!

!TwoDimMatrix methodsFor: 'private'!

errorBadDims
	^self error: 'matrices must have compatible dimensions'!

errorBadSize
	^self error: 'array is wrong size'!

errorNotSame
	^self error: 'matrices must be the same size'!

errorNotSquare
	^self error: 'matrix must be square'!

rows: rows columns: columns 
	"Sets the number of rows and columns in the matrix."

	matrix _ Array new: columns.
	1 to: columns do: [:cols | matrix at: cols put: (Array new: rows)]! !

!TwoDimMatrix methodsFor: 'printing'!

printOn: aStream

	matrix printOn: aStream! !

!TwoDimMatrix methodsFor: 'testing'!

= aTwoDimMatrix
	"Answer whether the receiver contains the same elements
	 as aTwoDimMatrix."

	| cols |
	(self sameSizeAs: aTwoDimMatrix) ifFalse: [^false].
	1 to: (self columns) do: [:cols |
		((self column: cols) = (aTwoDimMatrix column: cols)) ifFalse: [^false].
	].
	^true!

isCompatibleWith: aTwoDimMatrix
	"Answer whether the receiver has dimensions such that
	 it can be multiplied by aTwoDimMatrix."

	^(self columns = aTwoDimMatrix rows)!

isSquare
	"Answer whether the receiver is a square matrix."

	^self rows = self columns!

isZero
	"Answer whether the receiver contains all zeros."

	^self = (TwoDimMatrix rows: self rows columns: self columns) zero!

sameSizeAs: aMatrix
	"Answer whether the receiver has the same
	 dimensions as aMatrix."

	^((self rows = aMatrix rows) & (self columns = aMatrix columns))! !

!TwoDimMatrix methodsFor: 'parallel operations'!

lazyParallelAdd: aTwoDimMatrix
	"Answer a new TwoDimMatrix which contains futures to the sum of
	 the receiver and aTwoDimMatrix.  Only calculates a value for an
	 element when it is actually required.  Creates an error message
	 if the receiver and aTwoDimMatrix are not the same size."

	| newMatrix |
	(self sameSizeAs: aTwoDimMatrix) ifFalse: [^self errorNotSame].
	newMatrix _ TwoDimMatrix rows: (self rows) columns: (self columns).
	1 to: (self columns) do: [:cols | 
		1 to: (self rows) do: [:rows |
			newMatrix at: (cols@rows) put:
				[(self at: (cols@rows)) + (aTwoDimMatrix at: (cols@rows))] lazyValue
	]].
	^newMatrix!

lazyParallelMultiply: aTwoDimMatrix
	"Answers with a new matrix containing lazys to the product
	 of the receiver and aTwoDimMatrix. Only evaluates an
	 element of the matrix when it is needed.  Creates an error message
	 if the dimensions are not compatible."

	| newMatrix |
	(self isCompatibleWith: aTwoDimMatrix) ifFalse: [^self errorBadDims].
	newMatrix _ TwoDimMatrix rows: (self rows) columns: (aTwoDimMatrix columns).
	1 to: (self rows) do: [:r |
		1 to: (aTwoDimMatrix columns) do: [:c |
			newMatrix at: (c@r) put:
				([:x :y | (self row: y) fastDotProduct:
					(aTwoDimMatrix column: x)] lazyValue: c value: r).
	]].
	^newMatrix!

lazyParallelSubtract: aTwoDimMatrix
	"Answer a new TwoDimMatrix which contains futures to the difference
	 between the receiver and aTwoDimMatrix.  Only calculates a value for an
	 element when it is actually required.  Creates an error message
	 if the receiver and aTwoDimMatrix are not the same size."

	| newMatrix |
	(self sameSizeAs: aTwoDimMatrix) ifFalse: [^self errorNotSame].
	newMatrix _ TwoDimMatrix rows: (self rows) columns: (self columns).
	1 to: (self columns) do: [:cols | 
		1 to: (self rows) do: [:rows |
			newMatrix at: (cols@rows) put:
				[(self at: (cols@rows)) - (aTwoDimMatrix at: (cols@rows))] lazyValue
	]].
	^newMatrix!

parallelAdd: aTwoDimMatrix
	"Answer a new TwoDimMatrix which contains futures to the sum of
	 the receiver and aTwoDimMatrix.  Creates an error message
	 if the receiver and aTwoDimMatrix are not the same size."

	| newMatrix |
	(self sameSizeAs: aTwoDimMatrix) ifFalse: [^self errorNotSame].
	newMatrix _ TwoDimMatrix rows: (self rows) columns: (self columns).
	1 to: (self columns) do: [:cols | 
		1 to: (self rows) do: [:rows |
			newMatrix at: (cols@rows) put:
				[(self at: (cols@rows)) + (aTwoDimMatrix at: (cols@rows))] futureValue
	]].
	^newMatrix!

parallelMultiply: aTwoDimMatrix
	"Answers with a new matrix containing futures to the product
	 of the receiver and aTwoDimMatrix.  Always evaluates every
	 element of the matrix.  Creates an error message if the
	 dimensions are not compatible."

	| newMatrix |
	(self isCompatibleWith: aTwoDimMatrix) ifFalse: [^self errorBadDims].
	newMatrix _ TwoDimMatrix rows: (self rows) columns: (aTwoDimMatrix columns).
	1 to: (self rows) do: [:r |
		1 to: (aTwoDimMatrix columns) do: [:c |
			newMatrix at: (c@r) put:
			 ([:x :y | (self row: y) fastDotProduct:
				(aTwoDimMatrix column: x)] futureValue: c value: r)
	]].
	^newMatrix!

parallelSubtract: aTwoDimMatrix
	"Answer a new TwoDimMatrix which contains futures to the difference
	 between the receiver and aTwoDimMatrix.  Creates an error message
	 if the receiver and aTwoDimMatrix are not the same size."

	| newMatrix |
	(self sameSizeAs: aTwoDimMatrix) ifFalse: [^self errorNotSame].
	newMatrix _ TwoDimMatrix rows: (self rows) columns: (self columns).
	1 to: (self columns) do: [:cols | 
		1 to: (self rows) do: [:rows |
			newMatrix at: (cols@rows) put:
				[(self at: (cols@rows)) - (aTwoDimMatrix at: (cols@rows))] futureValue
	]].
	^newMatrix! !

!TwoDimMatrix methodsFor: 'filling'!

identity
	"Fill the receiver with all zeros, except for the
	 leading diagonal, which contains ones."

	self isSquare ifFalse: [^self errorNotSquare].
	self zero.
	1 to: (self columns) do: [:count | self at: (count@count) put: 1]!

random
	"Fill the receiver with random numbers."

	| rand |
	rand _ Random new.
	1 to: (self columns) do: [:cols |
		1 to: (self rows) do: [:rows |
			self at: cols@rows put: rand next]]!

zero
	"Set all the elements of the receiver to zero."

	1 to: (self columns) do: [:cols |
		1 to: (self rows) do: [:rows |
			self at: cols@rows put: 0]]! !

!TwoDimMatrix methodsFor: 'mathematical operations'!

* aTwoDimMatrix
	"Answers with a new matrix representing the product
	 of the receiver and aTwoDimMatrix.  Creates an error
	 message if the dimensions are not compatible."

	| newMatrix |
	(self isCompatibleWith: aTwoDimMatrix) ifFalse: [^self errorBadDims].
	newMatrix _ TwoDimMatrix rows: (self rows) columns: (aTwoDimMatrix columns).
	1 to: (self rows) do: [:r |
		1 to: (aTwoDimMatrix columns) do: [:c |
			newMatrix at: (c@r) put:
				((self row: r) fastDotProduct: (aTwoDimMatrix column: c)).
	]].
	^newMatrix!

+ aTwoDimMatrix
	"Answer a new TwoDimMatrix which is the sum of
	 the receiver and aTwoDimMatrix.  Create an error message
	 if the receiver and aTwoDimMatrix are not the same size."

	| newMatrix |
	(self sameSizeAs: aTwoDimMatrix) ifFalse: [^self errorNotSame].
	newMatrix _ TwoDimMatrix rows: (self rows) columns: (self columns).
	1 to: (self columns) do: [:cols | 
		1 to: (self rows) do: [:rows |
			newMatrix at: (cols@rows) put:
				((self at: (cols@rows)) + (aTwoDimMatrix at: (cols@rows)))
	]].
	^newMatrix!

- aTwoDimMatrix
	"Answer a new TwoDimMatrix which is the difference of
	 the receiver and aTwoDimMatrix.  Create an error message
	 if the receiver and aTwoDimMatrix are not the same size."

	| newMatrix |
	(self sameSizeAs: aTwoDimMatrix) ifFalse: [^self errorNotSame].
	newMatrix _ TwoDimMatrix rows: (self rows) columns: (self columns).
	1 to: (self columns) do: [:cols | 
		1 to: (self rows) do: [:rows |
			newMatrix at: (cols@rows) put:
				((self at: (cols@rows)) - (aTwoDimMatrix at: (cols@rows)))
	]].
	^newMatrix! !

!TwoDimMatrix methodsFor: 'accessing'!

at: aPoint
	"Answer the value contained in the matrix corresponding to
	 aPoint."

	| column |
	column _ matrix at: aPoint x.
	^column at: aPoint y!

at: aPoint put: aValue
	"Set the value in the matrix corresponding to aPoint."

	| column |
	column _ matrix at: aPoint x.
	column at: aPoint y put: aValue!

column: aNumber
	"Answer an array corresponding to the column given
	 by aNumber."

	^matrix at: aNumber!

column: aNumber from: anArray
	"Set the column in the receiver corresponding to aNumber
	 to the values from anArray."

	(self column: aNumber) size = (anArray size) ifFalse: [^self errorBadSize].
	1 to: anArray size do: [:index |
		self at: aNumber@index put: (anArray at: index)].!

columns
	"Answer the number of columns in the receiver."

	(matrix size = 0) ifTrue: [^0].
	^matrix size!

row: aNumber
	"Answer an array corresponding to the row given
	 by aNumber."

	| temp |
	temp _ Array new: (self columns).
	1 to: (self columns) do: [:col | temp at: col put: (self at: (col@aNumber))].
	^temp!

row: aNumber from: anArray
	"Set the row in the receiver corresponding to aNumber
	 to the values from anArray."

	(self row: aNumber) size = (anArray size) ifFalse: [^self errorBadSize].
	1 to: anArray size do: [:index |
		self at: index@aNumber put: (anArray at: index)].!

rows
	"Answer the number of rows in the receiver."

	(matrix size = 0) ifTrue: [^0].
	^matrix first size! !

"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

TwoDimMatrix class
	instanceVariableNames: ''!

!TwoDimMatrix class methodsFor: 'examples'!

example1
	"Illustrates the use of matrix addition."

	| a b |
	a _ TwoDimMatrix rows: 2 columns: 2.
	a row: 1 from: #(1 2).
	a row: 2 from: #(3 4).
	b _ TwoDimMatrix rows: 2 columns: 2.
	b column: 1 from: #(5 6).
	b column: 2 from: #(7 8).
	Transcript cr; show: (a + b) printString.

	"TwoDimMatrix example1."!

example2
	"Illustrates the use of matrix subtraction."

	| a b |
	a _ TwoDimMatrix rows: 2 columns: 2.
	a row: 1 from: #(1 2).
	a row: 2 from: #(3 4).
	b _ TwoDimMatrix rows: 2 columns: 2.
	b column: 1 from: #(5 6).
	b column: 2 from: #(7 8).
	Transcript cr; show: (b - a) printString.

	"TwoDimMatrix example2."!

example3
	"Illustrates the use of matrix operations.  Note that
	 matrices are *not* commutitive under multiplication,
	 so that a*b <> b*a."

	| a b |
	a _ TwoDimMatrix rows: 2 columns: 2.
	a row: 1 from: #(1 2).
	a row: 2 from: #(3 4).
	b _ TwoDimMatrix rows: 2 columns: 2.
	b column: 1 from: #(5 6).
	b column: 2 from: #(7 8).
	Transcript cr; show: ((a+b)*(a-b)) printString.
	Transcript cr; show: ((a*a) - (b*b)) printString.
	Transcript cr; show: (((a+b)*(a-b)) = ((a*a)-(b*b))) printString.

	"TwoDimMatrix example3."!

example4
	"Multiplies together two large matrices."

	| a1 a2 |
	a1 _ (TwoDimMatrix rows: 12 columns: 10) random.
	a2 _ (TwoDimMatrix rows: 10 columns: 11) random.
	Transcript cr; show: (Time millisecondsToRun: [a1 * a2]) printString.

	"TwoDimMatrix example4."!

example5
	"Illustrates a parallel matrix addition using futures.  The resulting
	 matrix is always fully evaluated, even if only one element is
	 required."

	| a1 a2 |
	a1 _ (TwoDimMatrix rows: 10 columns: 10) random.
	a2 _ (TwoDimMatrix rows: 10 columns: 10) random.
	Transcript cr; show: (Time millisecondsToRun: [(a1 parallelAdd: a2) at: 2@3]) printString

	"TwoDimMatrix example5."!

example6
	"Illustrates a parallel matrix addition using lazy evaluation.  The resulting
	 matrix is only evaluated when an element is actually required."

	| a1 a2 |
	a1 _ (TwoDimMatrix rows: 10 columns: 10) random.
	a2 _ (TwoDimMatrix rows: 10 columns: 10) random.
	Transcript cr; show: (Time millisecondsToRun: [(a1 lazyParallelAdd: a2) at: 2@3]) printString

	"TwoDimMatrix example6."!

example7
	"Illustrates the use of a Lazy-evaluated matrix multiplication.
	 Only the value actually requested is calculated."

	| a1 a2 |
	a1 _ (TwoDimMatrix rows: 10 columns: 10) random.
	a2 _ (TwoDimMatrix rows: 10 columns: 10) random.
	Transcript cr; show: ((a1 lazyParallelMultiply: a2) at: 7@5) printString.

	"TwoDimMatrix example7."!

example8
	"Illustrates the use of a Lazy-evaluated matrix multiplication.
	 Only the value actually requested is calculated.  Compare with
	 example9."

	| a1 a2 |
	a1 _ (TwoDimMatrix rows: 10 columns: 10) random.
	a2 _ (TwoDimMatrix rows: 10 columns: 10) random.
	Transcript cr; show: (Time millisecondsToRun: [
		((a1 lazyParallelMultiply: a2) at: 7@5) touch]) printString.

	"TwoDimMatrix example8."!

example9
	"Illustrates the use of a fully-evaluated parallel matrix multiplication.
	 All values are calculated.  Compare with example8."

	| a1 a2 |
	a1 _ (TwoDimMatrix rows: 10 columns: 10) random.
	a2 _ (TwoDimMatrix rows: 10 columns: 10) random.
	Transcript cr; show: (Time millisecondsToRun: [
		((a1 parallelMultiply: a2) at: 7@5) touch]) printString.

	"TwoDimMatrix example9."! !

!TwoDimMatrix class methodsFor: 'instance creation'!

identity: aNumber
	"Answer a new matrix which is square, and is all zero
	 except for the leading diagonal, which is 1."

	^(self new rows: aNumber columns: aNumber) identity!

rows: rows columns: columns 
	"Create a new instance of the receiver, with 
	 the number of rows and columns given."

	^super new rows: rows columns: columns!

zero: aNumber
	"Answer a new matrix which is square, and is all zero."

	^(self new rows: aNumber columns: aNumber) zero! !

'From Smalltalk-80, version 2, of April 1, 1983 on 9 April 1987 at 9:20:16 pm'!

!Number methodsFor: 'parallel intervals'!

to: stop asyncParallelDo: aBlock 
	"Create an Interval from the receiver up to the argument, stop,
	incrementing by 1.  For each element of the interval, evaluate the
	block, aBlock, in parallel.  Blocks are start immediately, and
	continue asynchronously."

	| nextValue aPE |
	nextValue _ self.
	aPE _ ParallelEvaluation new.
	[nextValue <= stop]
		whileTrue: 
			[aPE add: aBlock value: nextValue.
			 nextValue _ nextValue + 1]!

to: stop by: step asyncParallelDo: aBlock 
	"Create an Interval from the receiver up to the argument, stop,
	incrementing by step.  For each element of the interval, evaluate the
	block, aBlock, in parallel.  Blocks are started immediately, and
	continue asynchronously."

	| nextValue aPE |
	nextValue _ self.
	aPE _ ParallelEvaluation new.
	step < 0
		ifTrue: [[stop <= nextValue]
				whileTrue: 
					[aPE add: aBlock value: nextValue.
					nextValue _ nextValue + step]]
		ifFalse: [[stop >= nextValue]
				whileTrue: 
					[aPE add: aBlock value: nextValue.
					nextValue _ nextValue + step]]!

to: stop by: step parallelDo: aBlock 
	"Create an Interval from the receiver up to the argument, stop,
	incrementing by step.  For each element of the interval, evaluate the
	block, aBlock, in parallel.  Ensure that all blocks are completed before
	continuing."

	| nextValue aPE |
	nextValue _ self.
	aPE _ ParallelEvaluation new.
	step < 0
		ifTrue: [[stop <= nextValue]
				whileTrue: 
					[aPE add: aBlock value: nextValue.
					nextValue _ nextValue + step]]
		ifFalse: [[stop >= nextValue]
				whileTrue: 
					[aPE add: aBlock value: nextValue.
					nextValue _ nextValue + step]].
	aPE do		"ensure completion."!

to: stop parallelDo: aBlock 
	"Create an Interval from the receiver up to the argument, stop,
	incrementing by 1.  For each element of the interval, evaluate the
	block, aBlock, in parallel.  Ensure completion of all blocks before
	continuing."

	| nextValue aPE |
	nextValue _ self.
	aPE _ ParallelEvaluation new.
	[nextValue <= stop]
		whileTrue: 
			[aPE add: aBlock value: nextValue.
			 nextValue _ nextValue + 1].
	aPE do		"Ensure completion."! !

Transcript cr; show: 'Initializing Future'.
Future initialize.
Transcript cr; show: 'Initializing Lazy'.
Lazy inLazy inLed u

johnson@uiucdcsp.cs.uiuc.edu (08/24/87)

I think that futures were first used in Multilisp at MIT.  Whether that
system was the first to use them or not, there was a paper on futures
in Multilisp in TOPLAS a couple of years ago.  I seem to remember that
actor systems have used futures for awhile, too.

There was also a paper on futures in Smalltalk in OOPSLA'86.  I seemed
to have loaned out my copy of the proceedings, so I can't reference it
precisely.  However, the authors of the paper were Japanese and the title
of the paper was something like "Concurrent Smalltalk".  They did not
give any idea how futures were implemented, and gave them a different name.
They used atomic objects to limit parallelism.

It seems to me that futures and atomic objects are better mechanisms for
parallelism and synchronization for object-oriented programming than processes
and semaphores.  They encourage the programmer to think about the values
being passed rather than the flow of control.  Flow of control is usually
misleading, and seems especially so for object-oriented programming.

One of the features of Multilisp futures was that the interface to a future
was identical to that of their eventual values.  In other words, if a future
is going to return an integer then it can be added to another integer.
A process using the future is blocked if the future has not been evaluated.
The operation is resumed once the future "arrives".  Thus, a future could
be added to any Multilisp program by taking an expression and applying the
"future" function to it.  This made it very easy to turn a sequential program
into a parallel one.

If Smalltalk futures were like this then they would have to be objects
that could trap any message, check to see whether their value had been
calculated, and then forward the message to the value.  This is essentially
the same idea as an encapsulator, which was described in another OOPSLA'86
paper.  Encapsulators are implemented by overriding the doesNotUnderstand:
message.  Unfortunately, this message seems to be slow.  I am not sure if
this is just a byproduct of the interpreter that I am using.

I have thought of another mechanism that would make it easier to implement
special objects like futures and atomic objects---give each class its own
method lookup routine.  This would probably be stored in an instance variable
of the class.  Most classes have the same method lookup routine, but
encapsulator classes would each have their own.  An efficient interpreter
uses caching to reduce the number of method lookups, and method lookups are
expensive, so it doesn't make much difference if a method lookup takes another
memory reference or two.  If encapsulators like futures are used very much
then giving each class its own method lookup routine would probably improve
performance significantly.