[e-lang] Computing Pi in parallel with several VATs. How to write
it well ?
Mark Miller
markm at cs.jhu.edu
Fri Jul 22 18:17:01 EDT 2005
Hi Pascal, welcome to E! Sorry for the delay in responding.
Pascal Grange wrote:
> This code is too spagheti-like for me but I was not able to do it best. This
> spaghetiness is not a fact, just a feeling, for instance the printing of the
> result is in the middle of the code, not the end...and so on. I know that
> it's asynchronous but I would like to know if a E-programmer would code
> that in a drastically different E-way.
Here's how I would alter the code fragment in your email message to address
this issue.
pragma.enable("easy-return")
pragma.disable("explicit-result-guard")
pragma.enable("easy-when")
def [result,resolver] := Ref.promise()
var sum := 0 # sum often used below where "result" was originally used
for computer in computers {
# We provide job to a computer
if (currentInterval >= intervals.size()) {
# every interval has been submitted, we stop (this is the simple
# version)
break
}
def launch_compute() {
# This method launches a computation on computer.
# It "recursively" calls itself when the computation
# terminates so as to launch a new computation on the
# same computer. If there are no more intervals to compute
# then we stop computing and, if the computation has not
# been stopped yet, we mark it with finished to true and
# reveal the final result of the computation
computing += 1
def res := computer <- compute(currentInterval)
when (res) -> {
computing -= 1
sum += res
if (currentInterval < intervals.size()) {
launch_compute()
currentInterval += 1
} else if (computing == 0) {
# no-one is calculating nothing and
# nothing remains to be computed...it is the end.
resolver.resolve(sum)
}
} catch e {
e.printStackTrace()
}
}
launch_compute()
currentInterval += 1
}
when (result) -> {
println(`result : $result`)
}
The first part calculates and reveals a result, the second part reacts to a
revealed result. With this separation, the first part is now more amenable to
being packaged as a reusable abstraction.
> A version of this algorithm for Mandala is available at
> http://mandala.sf.net and I am convinced that my E implementation is
> biased by my knowledge of the mandala one. Moreover, I am not
> enough used with the E way of thinking ;)
>
> So would you, E thinkers and experts, write this code this way ? If not, how ?
> Appreciations about the spaghetiness of E programs in general and how to
> avoid or become used with it would be appreciated.
With this one small change to your code above, I'd say it now reads like
reasonable E code. Starting from this, I would seek to abstract the first part
into a more generically reusable asynchronous control abstraction; but I'd
guess that this abstraction issue is independent of E vs Mandala.
You can forward declare a variable with a "def" with no right hand side. You
can then bind this forward declaration with "bind". So, depending on taste,
you may instead prefer the rewrite
def result
...
} else if (computing == 0) {
# ...
bind result := sum
...
I would consider either style to be good E code.
> P.S. I have joined a tgz with my source code just for the ones who may want
> to try. It uses code from Mandala, especially the math-related code so you
> will need mandala(http://mandala.sf.net) to run this and you will also need
> to read my explanations in PiComputer.e and PiManager.e....
Some further points from the code in your attachment:
* The "# E Sample" comments from Walnut are only there to signal updoc that
this html <pre> block should be processed as updoc input. There's no need to
include this comment in E code.
* I prefer the spacing "} else {" to "}else{".
* You can parse a string representing an integer using the __makeInt function.
Together with the "bind" style mentioned above, your
var scale := 0
var nbIntervals := 0
try{
scale := <import:java.lang.Long>.parseLong(args[0])
nbIntervals := <import:java.lang.Long>.parseLong(args[1])
}catch e{
usage()
}
would become
def scale
def nbIntervals
try {
bind scale := __makeInt(args[0])
bind nbIntervals := __makeInt(args[1])
} catch e {
usage()
}
which reduces the number of assignable variables.
* I'm curious: Why do you use BigDecimal?
* Rather than saying "def random := <unsafe:java.security.SecureRandom>()",
you can just use the predefined "entropy".
* Many of the methods on Java's "StrictMath" class are available in E as
methods on floating point numbers. So your
<unsafe:java.lang.Math>.floor(scale / nbIntervals)
can be
(scale / nbIntervals).floor()
* Instead of
<unsafe:java.lang.System>.currentTimeMillis()
use
timer.now()
* Different E programmers differ in their preference for string concatenation
vs quasi-literals. MarcS prefers your
println("Computed in " + stopChrono + " milliseconds")
style, while I prefer
println(`Computed in $stopChrono milliseconds`)
> this is not a
> code I am proud of ;-) especially I made a trick with files to have the
> PiComputerManager find the PiComputers to avoid learning the E naming
> service...by the way, is there a naming service in E ?
Any naming service, in order to be secure, must be bootstrapped by out-of-band
introductions anyway. These out of band introductions invariably resemble your
filename trick. So for what you're doing, using your filename trick directly
is fine.
--
Text by me above is hereby placed in the public domain
Cheers,
--MarkM
More information about the e-lang
mailing list