Refactoring discovery

  • Follow


I had a calculation that effectively had half a dozen outputs.  When I
originally wrote it, I invented a method  for each result that would
calculate just that result.  This turned out be a dumb idea.  It
caused repeated logic and exploded the complexity.

What I have done now had greatly simplified the code. I have one
method that provides all the inputs and that calculates everything.
Then all the result-grabber methods are just simple getters.

The only downside is I must allocate a calculating object to hold the
results.

It has always seemed odd to me that most computer languages allow many
inputs but only one output.  Plumbing and wires don't behave that way.
-- 
Roedy Green Canadian Mind Products
http://mindprod.com
If you think it’s expensive to hire a professional to do the job, wait until you hire an amateur.
~ Red Adair

0
Reply Roedy 3/25/2011 7:22:08 AM

In message <kbgoo6dfrkh58r3ogel9nb6rekrs258it2@4ax.com>, Roedy Green wrote:

> It has always seemed odd to me that most computer languages allow many
> inputs but only one output.  Plumbing and wires don't behave that way.

There is the LISP way, with multivalued results which are not the same thing 
as tuples. Or there is the Python way, where a function can return a tuple 
which can be directly assigned to individual variables.
0
Reply Lawrence 3/25/2011 9:45:59 AM


Lawrence D'Oliveiro <ldo@geek-central.gen.new_zealand> wrote:
> In message <kbgoo6dfrkh58r3ogel9nb6rekrs258it2@4ax.com>, Roedy Green wrote:
> 
>> It has always seemed odd to me that most computer languages allow many
>> inputs but only one output.  Plumbing and wires don't behave that way.
> 
> There is the LISP way, with multivalued results which are not the same thing 
> as tuples. Or there is the Python way, where a function can return a tuple 
> which can be directly assigned to individual variables.

And there's the right way (yes, my biases are showing, I know):

  procedure Example
     (A   : in     Float;   
      B   : in out Float;
      C   : out    Float)

-- 
Leif Roar Moldskred
0
Reply Leif 3/25/2011 9:52:29 AM

In message <7v-dnf0DVrdA-BHQnZ2dnUVZ8783t52d@telenor.com>, Leif Roar 
Moldskred wrote:

> Lawrence D'Oliveiro <ldo@geek-central.gen.new_zealand> wrote:
>
>> In message <kbgoo6dfrkh58r3ogel9nb6rekrs258it2@4ax.com>, Roedy Green
>> wrote:
>> 
>>> It has always seemed odd to me that most computer languages allow many
>>> inputs but only one output.  Plumbing and wires don't behave that way.
>> 
>> There is the LISP way, with multivalued results which are not the same
>> thing as tuples. Or there is the Python way, where a function can return
>> a tuple which can be directly assigned to individual variables.
> 
> And there's the right way (yes, my biases are showing, I know):
> 
>   procedure Example
>      (A   : in     Float;
>       B   : in out Float;
>       C   : out    Float)

But that’s procedural, not functional.
0
Reply Lawrence 3/25/2011 11:01:16 AM

Lawrence D'Oliveiro wrote:
> In message<kbgoo6dfrkh58r3ogel9nb6rekrs258it2@4ax.com>, Roedy Green wrote:
>
>> It has always seemed odd to me that most computer languages allow many
>> inputs but only one output.  Plumbing and wires don't behave that way.
>
> There is the LISP way, with multivalued results which are not the same thing
> as tuples. Or there is the Python way, where a function can return a tuple
> which can be directly assigned to individual variables.

I believe that's an example of a false dichotomy.

   BugBear
0
Reply bugbear 3/25/2011 11:19:54 AM

Lawrence D'Oliveiro <ldo@geek-central.gen.new_zealand> wrote:
>
> But that’s procedural, not functional.

Well, yes. It would be a silly way to do it for a functional programming 
language. (Then again, it can be argued that for a functional programming
language it's silly to support multiple return values at all: it does go
rather against the grain of the whole "function" thing, after all.)

-- 
Leif Roar Moldskred
0
Reply leifm1143 (162) 3/25/2011 11:51:34 AM

Roedy Green wrote:
> It has always seemed odd to me that most computer languages allow many
> inputs but only one output.  Plumbing and wires don't behave that way.

Good thing Java allows you to return multiple outputs by putting them into a 
holder type, then.

-- 
Lew
Honi soit qui mal y pense.
http://upload.wikimedia.org/wikipedia/commons/c/cf/Friz.jpg
0
Reply noone7 (3512) 3/25/2011 11:51:34 AM

On 3/25/2011 3:22 AM, Roedy Green wrote:
> I had a calculation that effectively had half a dozen outputs.  When I
> originally wrote it, I invented a method  for each result that would
> calculate just that result.  This turned out be a dumb idea.  It
> caused repeated logic and exploded the complexity.
>
> What I have done now had greatly simplified the code. I have one
> method that provides all the inputs and that calculates everything.
> Then all the result-grabber methods are just simple getters.
>
> The only downside is I must allocate a calculating object to hold the
> results.
>
> It has always seemed odd to me that most computer languages allow many
> inputs but only one output.  Plumbing and wires don't behave that way.

     It seems to me a matter of notational convenience more than
anything else.  We write `q = y / z', a calculation with an
intentional but superficial resemblance to a mathematical
expression, and there's no place to describe what to do with
the division's remainder, a sometimes useful quantity that was
computed as a by-product.  `(q,r) = y / z', maybe?  It mightn't
be too awkward for quotient-and-remainder, but using the same
pattern for "half a dozen outputs" would be cumbersome and
error-prone, I think,  You'd want something that could attach
names to the pieces of the assignment target, and match them
up with names in the expression value.  Something like

	int r, g, b;
	(r.red, g.green, b.blue) = backgroundColor; // drop alpha

     FWIW, at least one dialect of Lisp supports multi-valued
expressions.  You can write the moral equivalent of

	x = f(a,b,c)

if you only care about the "principal" returned value, or the
moral equivalent of

	(x,y) = f(a,b,c)

if you want the ancillary value(s) as well.

-- 
Eric Sosman
esosman@ieee-dot-org.invalid
0
Reply Eric 3/25/2011 12:04:08 PM

In message <JMednbE0j8VbHBHQnZ2dnUVZ876dnZ2d@telenor.com>, Leif Roar 
Moldskred wrote:

> (Then again, it can be argued that for a functional programming
> language it's silly to support multiple return values at all: it does go
> rather against the grain of the whole "function" thing, after all.)

Why?
0
Reply Lawrence 3/25/2011 1:11:04 PM

In message <kbgoo6dfrkh58r3ogel9nb6rekrs258it2@4ax.com>, Roedy Green wrote:

> It has always seemed odd to me that most computer languages allow many
> inputs but only one output.  Plumbing and wires don't behave that way.

If you want to be able to express arbitrary dataflow connections, the only 
notationss I’m aware of that can deal with that are stack-based (e.g. 
POP-11, FORTH, PostScript). But such notations are notoriously error-prone.
0
Reply Lawrence 3/25/2011 1:12:44 PM

On Fri, 25 Mar 2011 08:04:08 -0400, Eric Sosman wrote:

> On 3/25/2011 3:22 AM, Roedy Green wrote:
>> I had a calculation that effectively had half a dozen outputs.  When I
>> originally wrote it, I invented a method  for each result that would
>> calculate just that result.  This turned out be a dumb idea.  It caused
>> repeated logic and exploded the complexity.
>>
>> What I have done now had greatly simplified the code. I have one method
>> that provides all the inputs and that calculates everything. Then all
>> the result-grabber methods are just simple getters.
>>
>> The only downside is I must allocate a calculating object to hold the
>> results.
>>
>> It has always seemed odd to me that most computer languages allow many
>> inputs but only one output.  Plumbing and wires don't behave that way.
> 
>      It seems to me a matter of notational convenience more than
> anything else.  We write `q = y / z', a calculation with an intentional
> but superficial resemblance to a mathematical expression, and there's no
> place to describe what to do with the division's remainder, a sometimes
> useful quantity that was computed as a by-product.  `(q,r) = y / z',
> maybe?

Clojure, a JVM language that can use Java libraries and be called from 
Java, has something like this:

(defn divide
  "Returns [q r] with the property that qy + r = x and
   0 <= r < abs(y)."
  [x y]
  (cond
    (and (< x 0) (< y 0))
    (let [[q r] (divide (- x) (- y))]
      [(inc q) (- (- y) r)])
    (< x 0)
    (let [[q r] (divide (- x) y)]
      [(dec (- q)) (- y r)])
    (< y 0)
    (let [[q r] (divide x (- y))]
      [(- q) r])
    :else
    (loop [q 0 r x]
      (if (> r y)
        (recur (inc q) (- r y))
        [q r]))))

(let [[q r] (divide a b)]
  (do-stuff-with q r))

(Of course, there are better ways to do integer division; this was just 
an example. Converting polar to Cartesian coordinates and back 
simultaneously would be a more realistic use case. Then again, (juxt quot 
rem) doesn't quite give the properties of the divide defined above -- the 
remainder ends up negative if x is negative.)

But it doesn't just push the multiple returns on the stack (as the JVM 
calling convention doesn't seem to allow that); it bundles them into a 
heap-allocated object (a vector, basically an unmodifiable ArrayList) and 
returns that. You could even do

(let [v (divide a b)]
  (do-array-list-stuff-with v))

if you wanted to.

>      FWIW, at least one dialect of Lisp supports multi-valued
> expressions.  You can write the moral equivalent of
> 
> 	x = f(a,b,c)
> 
> if you only care about the "principal" returned value, or the moral
> equivalent of
> 
> 	(x,y) = f(a,b,c)
> 
> if you want the ancillary value(s) as well.

Common Lisp has this, with somewhat warty syntax here and there. It uses 
the stack. You may also be able to use lists/vectors and destructuring as 
in Clojure, but that will again involve heap allocation of conses/vectors.

I don't see any way to avoid the holder object on the JVM. Clojure's 
destructuring and vector literals with runtime-variable elements may 
sugar up the syntax a bit but that's it.
0
Reply Ken 3/25/2011 1:41:14 PM

On 03/25/2011 03:22 AM, Roedy Green wrote:
> It has always seemed odd to me that most computer languages allow many
> inputs but only one output.  Plumbing and wires don't behave that way.

But mathematical functions return only one value; if you want to return 
more, you have to specify Cartesian products... which, to a large 
degree, is precisely what you do when you make holder objects. If you 
view programming languages as a descendant of mathematical notation, 
then this viewpoint makes logical sense.

-- 
Beware of bugs in the above code; I have only proved it correct, not 
tried it. -- Donald E. Knuth
0
Reply Joshua 3/25/2011 1:57:58 PM

Lawrence D'Oliveiro <ldo@geek-central.gen.new_zealand> wrote:
> In message <JMednbE0j8VbHBHQnZ2dnUVZ876dnZ2d@telenor.com>, Leif Roar 
> Moldskred wrote:
> 
>> (Then again, it can be argued that for a functional programming
>> language it's silly to support multiple return values at all: it does go
>> rather against the grain of the whole "function" thing, after all.)
> 
> Why?

Because functional programming tries (by and large) to treat computation
as progressing through mathematical(ish) functions -- which are a mapping
from one or more inputs to a single result. Multiple return values veers
from this mental model.

-- 
Leif Roar Moldskred
0
Reply Leif 3/25/2011 2:20:37 PM

Leif Roar Moldskred wrote:
> Lawrence D'Oliveiro<ldo@geek-central.gen.new_zealand>  wrote:
>> In message<JMednbE0j8VbHBHQnZ2dnUVZ876dnZ2d@telenor.com>, Leif Roar
>> Moldskred wrote:
>>
>>> (Then again, it can be argued that for a functional programming
>>> language it's silly to support multiple return values at all: it does go
>>> rather against the grain of the whole "function" thing, after all.)
>>
>> Why?
>
> Because functional programming tries (by and large) to treat computation
> as progressing through mathematical(ish) functions -- which are a mapping
> from one or more inputs to a single result. Multiple return values veers
> from this mental model.

Heh. Many functional programmers then end up building
quite large structures in passed parameters,
and then returning the whole thing.

It's still functional, but when a single "value"
can be a LISP list, it's not as single
as you first thought ;-)

   BugBear
0
Reply bugbear 3/25/2011 2:32:50 PM

On Fri, 25 Mar 2011 14:32:50 +0000, bugbear wrote:

> Leif Roar Moldskred wrote:
>> Lawrence D'Oliveiro<ldo@geek-central.gen.new_zealand>  wrote:
>>> In message<JMednbE0j8VbHBHQnZ2dnUVZ876dnZ2d@telenor.com>, Leif Roar
>>> Moldskred wrote:
>>>
>>>> (Then again, it can be argued that for a functional programming
>>>> language it's silly to support multiple return values at all: it does
>>>> go rather against the grain of the whole "function" thing, after
>>>> all.)
>>>
>>> Why?
>>
>> Because functional programming tries (by and large) to treat
>> computation as progressing through mathematical(ish) functions -- which
>> are a mapping from one or more inputs to a single result. Multiple
>> return values veers from this mental model.
> 
> Heh. Many functional programmers then end up building quite large
> structures in passed parameters, and then returning the whole thing.
> 
> It's still functional, but when a single "value" can be a LISP list,
> it's not as single as you first thought ;-)

And on the mathematical side there are vector-valued functions.

Another trick is returning hashes with key/value pairs -- mathematically 
equivalent to a function whose value is another function, which latter 
just happens to be defined pointwise. Especially in Clojure, where hashes 
can be invoked like functions and behave as pointwise defined functions 
on their keys, returning the associated values.

0
Reply Ken 3/25/2011 3:01:32 PM

On 3/25/2011 12:22 AM, Roedy Green wrote:
> I had a calculation that effectively had half a dozen outputs.  When I
> originally wrote it, I invented a method  for each result that would
> calculate just that result.  This turned out be a dumb idea.  It
> caused repeated logic and exploded the complexity.
>
> What I have done now had greatly simplified the code. I have one
> method that provides all the inputs and that calculates everything.


I think it would elucidate your refactoring pattern more if you included 
an example.  What you did post is very general:

"I made a thing.  Then I refactored and it was better."

Yay?


0
Reply markspace 3/25/2011 4:43:56 PM

On 11-03-25 10:12 AM, Lawrence D'Oliveiro wrote:
> In message <kbgoo6dfrkh58r3ogel9nb6rekrs258it2@4ax.com>, Roedy Green wrote:
> 
>> It has always seemed odd to me that most computer languages allow many
>> inputs but only one output.  Plumbing and wires don't behave that way.
> 
> If you want to be able to express arbitrary dataflow connections, the only 
> notationss I’m aware of that can deal with that are stack-based (e.g. 
> POP-11, FORTH, PostScript). But such notations are notoriously error-prone.

If you want to do dataflow then the best way to do it is using a
dataflow language. I've used a few of them, including LabVIEW and
Prograph CPX (which latter was pretty awesome actually). And one can
argue that stitching together UNIX tools with tees and pipes and
redirects is a form of dataflow.

AHS
-- 
That's not the recollection that I recall...All this information is
certainly in the hands of the auditor and we certainly await his report
to indicate what he deems has occurred.
-- Halifax, Nova Scotia mayor Peter Kelly, who is currently deeply in
the shit
0
Reply Arved 3/25/2011 9:09:08 PM

In message <Vv7jp.1732$in3.386@newsfe17.iad>, Arved Sandstrom wrote:

> If you want to do dataflow then the best way to do it is using a
> dataflow language. I've used a few of them, including LabVIEW and
> Prograph CPX (which latter was pretty awesome actually).

You mean graphical languages? I’m not sure how well they scale. Also there’s 
the problem that standard source manipulations like diff/patch and merge are 
lacking.

> And one can argue that stitching together UNIX tools with tees and pipes
> and redirects is a form of dataflow.

That can express fan-out, but I don’t think it can express subsequent fan-in 
from that fan-out.

The standard solution to this is to introduce names for the connections 
somewhere. Which basically brings us right back to functional programming.
0
Reply ldo (2144) 3/26/2011 2:39:05 AM

In message <imi0cq$ah4$1@dont-email.me>, Eric Sosman wrote:

> You'd want something that could attach names to the pieces of the
> assignment target, and match them up with names in the expression value. 
> Something like
> 
> int r, g, b;
> (r.red, g.green, b.blue) = backgroundColor; // drop alpha

In Python you can write

    r, g, b = (getattr(backgroundColor, f) for f in ("red", "green", "blue"))

But really, is it such a big deal? In conventional languages like C we
happily write

    q = y / z;
    r = y % z;

and leave it to the compiler to do the dataflow analysis for us.

0
Reply ldo (2144) 3/26/2011 2:44:44 AM

On Fri, 25 Mar 2011 07:51:34 -0400, Lew <noone@lewscanon.com> wrote,
quoted or indirectly quoted someone who said :

>Good thing Java allows you to return multiple outputs by putting them into a 
>holder type, then.

You could implement a language with a single input too. You must
construct an artificial (that has no meaning other than holder for
some function) object if you need more than one parameter. Gaak! you
say. How inconvenient!  That's how I feel about requiring dummy holder
objects for multiple outputs.

What is so different about in input and output?  Why should they be
treated so differently? In a beautiful language the syntax should as
parallel as possible.

Underneath, at the JVM level, they could be treated FORTH style, just
have the caller push the parms on the stack and have the callee pop
the inputs and push the results to the stack, or perhaps use two
stacks, one for inputs and one for outputs.


-- 
Roedy Green Canadian Mind Products
http://mindprod.com
There are only two industries that refer to their customers as "users".
~ Edward Tufte

0
Reply Roedy 3/26/2011 4:43:20 AM

On Fri, 25 Mar 2011 09:43:56 -0700, markspace <-@.> wrote, quoted or
indirectly quoted someone who said :

>I think it would elucidate your refactoring pattern more if you included 
>an example.  What you did post is very general:

You can see the new code at
http://mindprod.com/applet/canadiantax.html 

The class in question is CalculateCanadianTaxes

The outputs include GST, HST, PST tax rates, GST, HST, PST tax
amounts, total tax, and total payable.

-- 
Roedy Green Canadian Mind Products
http://mindprod.com
There are only two industries that refer to their customers as "users".
~ Edward Tufte

0
Reply Roedy 3/26/2011 4:50:33 AM

On 11-03-25 11:39 PM, Lawrence D'Oliveiro wrote:
> In message <Vv7jp.1732$in3.386@newsfe17.iad>, Arved Sandstrom wrote:
> 
>> If you want to do dataflow then the best way to do it is using a
>> dataflow language. I've used a few of them, including LabVIEW and
>> Prograph CPX (which latter was pretty awesome actually).
> 
> You mean graphical languages? I’m not sure how well they scale. Also there’s 
> the problem that standard source manipulations like diff/patch and merge are 
> lacking.

Plenty of others have textual source. Oz for example.

Also, I'm not sure what you mean by "scale" in this context.

>> And one can argue that stitching together UNIX tools with tees and pipes
>> and redirects is a form of dataflow.
> 
> That can express fan-out, but I don’t think it can express subsequent fan-in 
> from that fan-out.

In many cases you could do it in a shell but you'd have to get quite
creative. I'm not claiming in any case that UNIX system and shell offers
a full, high-powered dataflow environment.

> The standard solution to this is to introduce names for the connections 
> somewhere. Which basically brings us right back to functional programming.

The "names" are single-assignment (logic) dataflow variables, that are
either unbound or bound. Computations express their dependency on
inputs; specifying the connections is a matter of indicating that
certain inputs are outputs of another computation.

It's not so much that _this_ is functional programming, but more that
many functional programming languages allow you to program in such a way
that it resembles dataflow.

AHS
-- 
That's not the recollection that I recall...All this information is
certainly in the hands of the auditor and we certainly await his report
to indicate what he deems has occurred.
-- Halifax, Nova Scotia mayor Peter Kelly, who is currently deeply in
the shit
0
Reply Arved 3/26/2011 12:04:01 PM

On 26 Mrz., 05:43, Roedy Green <see_webs...@mindprod.com.invalid>
wrote:
> On Fri, 25 Mar 2011 07:51:34 -0400, Lew <no...@lewscanon.com> wrote,
> quoted or indirectly quoted someone who said :
>
> >Good thing Java allows you to return multiple outputs by putting them into a
> >holder type, then.
>
> You could implement a language with a single input too.

Languages which can use single input and output exist
already. The Unix pipes are an example of such a logic:

  cat file | sort | uniq | more

Another example comes from OO. Applying a method to the
result of another method. E.g.:

  aVariable = anObject.method1().method2().method3();

Even non OO languages can support this:

  aVariable := func4(func3(func2(func1(aValue))));

I see no reason to implement a new language to support
single input and output for functions.


Greetings Thomas Mertes

--
Seed7 Homepage:  http://seed7.sourceforge.net
Seed7 - The extensible programming language: User defined statements
and operators, abstract data types, templates without special
syntax, OO with interfaces and multiple dispatch, statically typed,
interpreted or compiled, portable, runs under linux/unix/windows.
0
Reply tm 3/26/2011 12:42:59 PM

On 03/26/2011 08:42 AM, tm wrote:
> Languages which can use single input and output exist
> already. The Unix pipes are an example of such a logic:

So's Java.  So's C.  So's Prolog, for suitable understandings of "input" and 
"output".

>    cat file | sort | uniq | more
>
> Another example comes from OO. Applying a method to the

Such as Java, or C#.

> result of another method. E.g.:
>
>    aVariable = anObject.method1().method2().method3();
>
> Even non OO languages can support this:
>
>    aVariable := func4(func3(func2(func1(aValue))));

Such as Fortran or COBOL.

> I see no reason to implement a new language to support
> single input and output for functions.

Since every computer language already supports the feature, and few restrict 
you to it, your conclusion is demonstrably valid.

-- 
Lew
Honi soit qui mal y pense.
http://upload.wikimedia.org/wikipedia/commons/c/cf/Friz.jpg
0
Reply Lew 3/26/2011 12:51:03 PM

On 3/25/2011 9:43 PM, Roedy Green wrote:
> On Fri, 25 Mar 2011 07:51:34 -0400, Lew<noone@lewscanon.com>  wrote,
> quoted or indirectly quoted someone who said :
>
>> Good thing Java allows you to return multiple outputs by putting them into a
>> holder type, then.
>
> You could implement a language with a single input too.


This is a point.  Java already has such a "single input" construct, the 
var-args:

   void method( SomeType ... va ) { ...

I wonder if this pattern could be adapted to return types as well?


> You must
> construct an artificial (that has no meaning other than holder for
> some function) object if you need more than one parameter. Gaak! you
> say. How inconvenient!  That's how I feel about requiring dummy holder
> objects for multiple outputs.


Let's see, you'd have to declare a "var-args return type:"

  SomeType ... method( X x ) { ...

To use this, you might return an array directly:

  SomeType ... method( X x ) {
     SomeType[] st;
     ...
     return st;
  }

Or you might return a list of objects, and let the compile box them up 
in to an array:

  SomeType ... method( X x ) {
     SomeType a, b, c, d;
     ...
     return a, b, d;
  }

To call this method, you'd either assign the result to the exact return 
type (an array).

   SomeType[] st = method(x);

Or you could have the compiler unbox the values for you.  Unused values 
are tossed away, and values which don't exist are null.

   SomeType u, v, w, y;

   u, v, w, y = method(x);  // y will be null in this example

The only disadvantage that I can see would be folks who want to return 
different type values would be tempted declare a return type of Object 
var-args, which would remove a lot of the benefit of strong typing in Java.

   Object ... method( Y y );  // ick!

Optimization (e.g., return values stored to the stack instead of an 
explicit array) are left up to the compiler.

0
Reply markspace 3/26/2011 4:01:38 PM

Eric Sosman <esosman@ieee-dot-org.invalid> writes:
>FWIW, at least one dialect of Lisp supports multi-valued
>expressions.  You can write the moral equivalent of
>x = f(a,b,c)
>if you only care about the "principal" returned value, or the
>moral equivalent of
>(x,y) = f(a,b,c)
>if you want the ancillary value(s) as well.

  It's called �Perl�:

#!/usr/bin/perl
#perl 5.8.3

use strict;
use warnings;

sub f(){ return wantarray ?( 1, 2 ): 3; }

my( $a, $b )= f();
my $c = f();

print "$a, $b\n$c\n";

1, 2
3

0
Reply ram 3/26/2011 7:15:17 PM

Leif Roar Moldskred <leifm@dimnakorr.com> writes:
>And there's the right way (yes, my biases are showing, I know):
>procedure Example
>   (A   : in     Float;   
>    B   : in out Float;
>    C   : out    Float)

  Mmh, this reminds me of Ada.

  Here is the entry from my personal FAQ about �multiple-return
  values� in Java:

      �You can't return more than one value from a method.
      If you want to, you have to return a little array (unless 
      one value is an int and the other is a Person!) or an object
      of some special little class made just for this purpose.

      When I was helping Bill Joy and Guy L. Steele Jr. by
      reviewing drafts of the original Java Language
      Specification, I was originally upset that there was no
      way to do this. So I set out to find a small example
      program that obviously demanded such a feature, to
      convince them that multiple value returns must be added.

      I was unable to come up with one, and I could see that
      Java's philosphy was to leave out things that are rarely
      used and not crucial, so finally didn't say anything.�

   Dan Weinreb's Weblog

http://dlweinreb.wordpress.com/category/java/

  I have written several programs myself to show how multiple
  returns might be emulated in Java. Each approach has its
  advantages and disadvantages. I might add that, whenever I
  want to achieve something with Java, the lack of explicit
  multiple return values is not a problem for me.

  I assume a simple multiple-return task such as, in pseudocode:

operation "sumdiff"
in        x, y;
out       sum, difference;
{ sum = x + y; difference = x - y; }

  Solution with public fields:

class Sumdiff
{ public Sumdiff( final int x, final int y )
  { this.sum = x + y; this.difference = x - y; }
  public final int sum; public final int difference; }

public class Main
{ public static void main( final java.lang.String[] args )
  { final Sumdiff result = new Sumdiff( 4, 2 ); 
    java.lang.System.out.println
    ( result.sum + ", " + result.difference ); }}

6, 2

  If you do not like public fields, you might use getters
  as well.

  A �processor object� can be created once and be used
  several times:

public class Main
{ public static void main( final java.lang.String[] args )
  { final Processor processor = new Processor(); 
    processor.set( 4, 2 );
    processor.calculateSumDiff();
    java.lang.System.out.println( processor.getSum() );
    java.lang.System.out.println( processor.getDifference() ); 
    processor.set( 8, 4 );
    processor.calculateSumDiff();
    java.lang.System.out.println( processor.getSum() );
    java.lang.System.out.println( processor.getDifference() ); }}

class Processor
{ public void set( final int x, final int y )
  { this.x = x; this.y = y; }
  public void calculateSumDiff()
  { this.sum = x + y; this.difference = x - y; }
  public java.lang.Integer getSum(){ return sum; }
  public java.lang.Integer getDifference(){ return difference; }
  int x; int y; int sum; int difference; }

  To avoid allocation overhead of a result object,
  the client might provide and reuse such an object:

class Result { public int x; public int y; }

class Server
{ void run( final Result result, final int x, final int y )
  { result.x = x + y; result.y = x - y; }}

public final class Main
{ private static Result result = new Result(); /* single allocation */
  public static void main( final java.lang.String argv[] )
  { Server server = new Server();
    server.run( result, 1, 2 );
    java.lang.System.out.println( result.x );
    java.lang.System.out.println( result.y ); 
    server.run( result, 3, 4  );
    java.lang.System.out.println( result.x );
    java.lang.System.out.println( result.y ); }}

  One can also emulate multiple returns via multiple
  arguments, but only when adopting a �continuation passing
  style�. In the next example, the server �returns� a pair
  of random numbers to the client, by calling back a method
  provided by the client.

interface Client { void continuation( int x, int y ); }

class Server
{ static java.util.Random rand = new java.util.Random();
  static void getPair( final Client client )
  { client.continuation( rand.nextInt( 11 ), rand.nextInt( 21 )); }}

class Example implements Client
{ public void continuation( final int x, final int y )
  { java.lang.System.out.println( x + ", " + y ); }
  public void main()
  { Server.getPair( this ); }}

public class Main
{ public static void main( final java.lang.String[] args )  
  { new Example().main(); }}

  But, as said, I rarely ever (actually: never) have needed any
  of these multiple return value emulations in my own projects.

  Possibly this is because I already have accounted for the
  properties and limitations of Java when I was designing
  my classes. So I have designed them from the start in such
  a way that multiple return values are not needed.

0
Reply ram 3/26/2011 7:28:53 PM

Joshua Cranmer <Pidgeot18@verizon.invalid> writes:
>But mathematical functions return only one value; 

  In mathematics, there is no clear-drawn distinction between
  �one value� and �multiple values�. For example, one 
  says that f, defined as f(x,y):=x+y, is a function of
  �two arguments�, yet one usually gives �f: R � R -> R�,
  which literally means: it is a function of /one/ argument,
  which happens to be a pair. And of course, one can have
  functions ... -> R x R.

0
Reply ram 3/26/2011 7:43:02 PM

On 03/26/2011 03:43 PM, Stefan Ram wrote:
> Joshua Cranmer<Pidgeot18@verizon.invalid>  writes:
>> But mathematical functions return only one value;
>
>    In mathematics, there is no clear-drawn distinction between
>    �one value� and �multiple values�. For example, one
>    says that f, defined as f(x,y):=x+y, is a function of
>    �two arguments�, yet one usually gives �f: R � R ->  R�,
>    which literally means: it is a function of /one/ argument,
>    which happens to be a pair. And of course, one can have
>    functions ... ->  R x R.

That's what I said in the clause just after that semicolon: "if you want 
more, you have to specify Cartesian products."

-- 
Beware of bugs in the above code; I have only proved it correct, not 
tried it. -- Donald E. Knuth
0
Reply Joshua 3/26/2011 7:44:53 PM

Joshua Cranmer <Pidgeot18@verizon.invalid> writes:
>That's what I said in the clause just after that semicolon: "if you want 
>more, you have to specify Cartesian products."

  Sorry, I have overlooked this.

0
Reply ram 3/26/2011 7:48:09 PM

In message <return-20110326202555@ram.dialup.fu-berlin.de>, Stefan Ram wrote:

>       When I was helping Bill Joy and Guy L. Steele Jr. by
>       reviewing drafts of the original Java Language
>       Specification, I was originally upset that there was no
>       way to do this. So I set out to find a small example
>       program that obviously demanded such a feature, to
>       convince them that multiple value returns must be added.
> 
>       I was unable to come up with one, and I could see that
>       Java's philosphy was to leave out things that are rarely
>       used and not crucial, so finally didn't say anything.«

Funny, I can think of any number of examples. Such as this
<http://www.codecodex.com/wiki/Reading_time_zone_files>:

   (sig, ver, tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt, tzh_timecnt, tzh_typecnt, tzh_charcnt) = \
       structread(zonefile, ">4s1B15x6I")

0
Reply Lawrence 3/27/2011 12:53:04 AM

On 11-03-26 09:53 PM, Lawrence D'Oliveiro wrote:
> In message <return-20110326202555@ram.dialup.fu-berlin.de>, Stefan Ram wrote:
> 
>>       When I was helping Bill Joy and Guy L. Steele Jr. by
>>       reviewing drafts of the original Java Language
>>       Specification, I was originally upset that there was no
>>       way to do this. So I set out to find a small example
>>       program that obviously demanded such a feature, to
>>       convince them that multiple value returns must be added.
>>
>>       I was unable to come up with one, and I could see that
>>       Java's philosphy was to leave out things that are rarely
>>       used and not crucial, so finally didn't say anything.«
> 
> Funny, I can think of any number of examples. Such as this
> <http://www.codecodex.com/wiki/Reading_time_zone_files>:
> 
>    (sig, ver, tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt, tzh_timecnt, tzh_typecnt, tzh_charcnt) = \
>        structread(zonefile, ">4s1B15x6I")
> 
That's not a compelling example. Those return values belong together,
and you're gaining nothing from breaking them out into a tuple as
opposed to having them be fields of an object. Which was rather the
point of Stefan's post.

This example of yours is no better than one where you might return a
tuple for the two coordinates of a point. As if those are truly
independent multiple return values.

AHS
-- 
That's not the recollection that I recall...All this information is
certainly in the hands of the auditor and we certainly await his report
to indicate what he deems has occurred.
-- Halifax, Nova Scotia mayor Peter Kelly, who is currently deeply in
the shit
0
Reply Arved 3/27/2011 1:28:38 AM

In message <5pwjp.2224$9j5.1777@newsfe04.iad>, Arved Sandstrom wrote:

> On 11-03-26 09:53 PM, Lawrence D'Oliveiro wrote:
>> In message <return-20110326202555@ram.dialup.fu-berlin.de>, Stefan Ram
>> wrote:
>> 
>>>       When I was helping Bill Joy and Guy L. Steele Jr. by
>>>       reviewing drafts of the original Java Language
>>>       Specification, I was originally upset that there was no
>>>       way to do this. So I set out to find a small example
>>>       program that obviously demanded such a feature, to
>>>       convince them that multiple value returns must be added.
>>>
>>>       I was unable to come up with one, and I could see that
>>>       Java's philosphy was to leave out things that are rarely
>>>       used and not crucial, so finally didn't say anything.«
>> 
>> Funny, I can think of any number of examples. Such as this
>> <http://www.codecodex.com/wiki/Reading_time_zone_files>:
>> 
>>    (sig, ver, tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt, tzh_timecnt, tzh_typecnt, tzh_charcnt) = \
>>        structread(zonefile, ">4s1B15x6I")
>> 
> That's not a compelling example. Those return values belong together,
> and you're gaining nothing from breaking them out into a tuple ...

Yes you are. Look at the rest of the code.
0
Reply Lawrence 3/27/2011 3:08:49 AM

On 11-03-27 12:08 AM, Lawrence D'Oliveiro wrote:
> In message <5pwjp.2224$9j5.1777@newsfe04.iad>, Arved Sandstrom wrote:
> 
>> On 11-03-26 09:53 PM, Lawrence D'Oliveiro wrote:
>>> In message <return-20110326202555@ram.dialup.fu-berlin.de>, Stefan Ram
>>> wrote:
>>>
>>>>       When I was helping Bill Joy and Guy L. Steele Jr. by
>>>>       reviewing drafts of the original Java Language
>>>>       Specification, I was originally upset that there was no
>>>>       way to do this. So I set out to find a small example
>>>>       program that obviously demanded such a feature, to
>>>>       convince them that multiple value returns must be added.
>>>>
>>>>       I was unable to come up with one, and I could see that
>>>>       Java's philosphy was to leave out things that are rarely
>>>>       used and not crucial, so finally didn't say anything.«
>>>
>>> Funny, I can think of any number of examples. Such as this
>>> <http://www.codecodex.com/wiki/Reading_time_zone_files>:
>>>
>>>    (sig, ver, tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt, tzh_timecnt, tzh_typecnt, tzh_charcnt) = \
>>>        structread(zonefile, ">4s1B15x6I")
>>>
>> That's not a compelling example. Those return values belong together,
>> and you're gaining nothing from breaking them out into a tuple ...
> 
> Yes you are. Look at the rest of the code.

I saw the rest of the code. Given that each of those return values is
called within 10 statements of where they were created, and only used
once or twice, and this is a one-off, the marginal convenience of using
the tuple isn't outweighed by the fragmentation it causes. This is
probably why you think it's OK.

For more maintainable code - in any language that supports it - you'd be
better off initializing an object with the zonefile information and
returning the specific information with methods or properties. Even a
named tuple would be better.

AHS

-- 
That's not the recollection that I recall...All this information is
certainly in the hands of the auditor and we certainly await his report
to indicate what he deems has occurred.
-- Halifax, Nova Scotia mayor Peter Kelly, who is currently deeply in
the shit
0
Reply Arved 3/27/2011 4:05:38 AM

In message <gIyjp.4685$sj5.4444@newsfe09.iad>, Arved Sandstrom wrote:

> I saw the rest of the code. Given that each of those return values is
> called within 10 statements of where they were created, and only used
> once or twice, and this is a one-off, the marginal convenience of using
> the tuple isn't outweighed by the fragmentation it causes.

What is this “fragmentation” of which you speak?

“The concert was like the First World War. Sure we had heavy metal then, but 
we called it shrapnel in those days.”
    -- Alexei Sayle

> For more maintainable code - in any language that supports it - you'd be
> better off initializing an object with the zonefile information and
> returning the specific information with methods or properties.

In case you didn’t notice, that code WAS for the construction of such an 
object.
0
Reply Lawrence 3/27/2011 6:10:57 AM

On 25/03/2011 10:44 PM, Lawrence D'Oliveiro wrote:
> In message<imi0cq$ah4$1@dont-email.me>, Eric Sosman wrote:
>> You'd want something that could attach names to the pieces of the
>> assignment target, and match them up with names in the expression value.
>> Something like
>> int r, g, b;
>> (r.red, g.green, b.blue) = backgroundColor; // drop alpha
>
> In Python you can write
>      r, g, b = (getattr(backgroundColor, f) for f in ("red", "green", "blue"))
> But really, is it such a big deal? In conventional languages like C we
> happily write
>
>      q = y / z;
>      r = y % z;
>
> and leave it to the compiler to do the dataflow analysis for us.

Quotient and remainder of division are a very special case. The hardware 
sometimes has a single operation that produces both, and knowledge that 
/ and % are closely related operations is built into the language.  With
    first = longComplexOperation(lots of arguments)
    second = anotherLongComplexOperation(same set of arguments)
it's going to take a very sophisticated compiler to synthesize
    (first, second) = combinedLongOperation(same set of arguments)
and it's going to have to solve the Halting Problem, too.

We already write "combined operations" in other contexts.
if (x.hasKey(y)) then x.addPair(y, 
someVeryBigThingIDidn'tWantToSysnthesizeUnlessIHadTo)

sometimes gets implemented as
   whereToInsert = x.findLocation(y);
   if (whereToInsert.keyIsntAlreadyThere())
     whereToInsert.addPair(y,...)
Or possibly whereToInsert is internal state in x and x.addPair(y,z) is 
x.addAtSpotDeterminedByLastHasKey(z).

0
Reply dalamb (181) 3/30/2011 8:10:48 PM

On 26/03/2011 3:43 PM, Stefan Ram wrote:
> Joshua Cranmer<Pidgeot18@verizon.invalid>  writes:
>> But mathematical functions return only one value;
>
>    In mathematics, there is no clear-drawn distinction between
>    �one value� and �multiple values�. For example, one
>    says that f, defined as f(x,y):=x+y, is a function of
>    �two arguments�, yet one usually gives �f: R � R ->  R�,
>    which literally means: it is a function of /one/ argument,
>    which happens to be a pair. And of course, one can have
>    functions ... ->  R x R.
>

And out of left field comes currying and/or closures:
    g: x -> (y -> z)
where (f(x))(y) = g(x,y).  But it's sometimes annoying that leaving 
unbound the "second argument" of what you though of as a 2-argument 
function is privileged over leaving the 1st argument unbound.
0
Reply dalamb (181) 3/30/2011 8:15:26 PM

On 26/03/2011 12:50 AM, Roedy Green wrote:
> On Fri, 25 Mar 2011 09:43:56 -0700, markspace<-@.>  wrote, quoted or
> indirectly quoted someone who said :
>
>> I think it would elucidate your refactoring pattern more if you included
>> an example.  What you did post is very general:
>
> You can see the new code at
> http://mindprod.com/applet/canadiantax.html
>
> The class in question is CalculateCanadianTaxes
>
> The outputs include GST, HST, PST tax rates, GST, HST, PST tax
> amounts, total tax, and total payable.

GHAH.  Conceptually that's really
    CalculateCanadianTaxes: farTooManyInputs -> farTooManyOutputs
Single, vastly complex input; single, vastly complex output.  I'm 
serious; I can't see how anyone can think of the results of a tax 
computation as anything other than one big record type.

Much nicer is the Ohio state tax form from around 1975:
   OhioTax = federalTax * factorLessThanOne
But that's a whole other issue.

0
Reply dalamb (181) 3/30/2011 8:21:01 PM

On 30/03/2011 4:10 PM, David Lamb wrote:
> first = longComplexOperation(lots of arguments)
> second = anotherLongComplexOperation(same set of arguments)
> it's going to take a very sophisticated compiler to synthesize
> (first, second) = combinedLongOperation(same set of arguments)
> and it's going to have to solve the Halting Problem, too.

I forgot to mention, the two sets of arguments might each be overlapping 
subsets of a larger set of arguments, making the commonality even harder 
to detect automatically.  The designer really has to write 
combinedLongOperation(LikelyOneObjectWithFields) that returns 
AllTheResults object.
0
Reply dalamb (181) 3/30/2011 8:25:13 PM

On 03/30/2011 04:10 PM, David Lamb wrote:
> We already write "combined operations" in other contexts.
> if (x.hasKey(y)) then x.addPair(y,
> someVeryBigThingIDidn'tWantToSysnthesizeUnlessIHadTo)

Well, another way to implement this is:

public boolean hasKey(Y y) {
   LocPtr ptr = getWhereToInsert(y);
   this.cachedPtr = ptr;
   return ptr.valid();
}

public void addPair(Y y, Object obj) {
   LocPtr ptr;
   if (cachedPtr && cachedPtr.y == y)
     ptr = cachedPtr;
   else
     ptr = getWhereToInsert(y);
   ptr.insert(obj);
}

Which pretty much gets you the "benefit" of combining them into one 
single operation without needing to provide all of the back-level 
predicate code operations (set-if-present, set-if-not-present, etc.)
-- 
Beware of bugs in the above code; I have only proved it correct, not 
tried it. -- Donald E. Knuth
0
Reply Pidgeot18 (1393) 3/30/2011 9:31:53 PM

On 3/30/2011 2:31 PM, Joshua Cranmer wrote:

> if (cachedPtr && cachedPtr.y == y)

> Which pretty much gets you the "benefit" of combining them into one
> single operation without needing to provide all of the back-level
> predicate code operations (set-if-present, set-if-not-present, etc.)

Generally, this idea seems similar to techniques used in dynamic 
programming, which is itself a very powerful paradigm.  Useful to know 
about, at least.

http://en.wikipedia.org/wiki/Dynamic_programming
0
Reply markspace 3/30/2011 10:52:54 PM

Joshua Cranmer wrote:
> Well, another way to implement this is:
>
> public boolean hasKey(Y y) {
>   LocPtr ptr = getWhereToInsert(y);
>   this.cachedPtr = ptr;
>   return ptr.valid();
> }
>
> public void addPair(Y y, Object obj) {
>   LocPtr ptr;
>   if (cachedPtr && cachedPtr.y == y)

Umm, that won't compile in Java.

>     ptr = cachedPtr;
>   else
>     ptr = getWhereToInsert(y);
>   ptr.insert(obj);
> }
>
> Which pretty much gets you the "benefit" of combining them into one single
> operation without needing to provide all of the back-level predicate code
> operations (set-if-present, set-if-not-present, etc.)

Unless you're in multi-threaded land.  In that case, don't think tacking 
'synchronized' onto your method definitions will help.

-- 
Lew
0
Reply noone7 (3512) 3/31/2011 3:58:02 AM

In message <RCkjp.2728$%f5.828@newsfe08.iad>, Arved Sandstrom wrote:

> On 11-03-25 11:39 PM, Lawrence D'Oliveiro wrote:
>
>> In message <Vv7jp.1732$in3.386@newsfe17.iad>, Arved Sandstrom wrote:
>> 
>>> If you want to do dataflow then the best way to do it is using a
>>> dataflow language. I've used a few of them, including LabVIEW and
>>> Prograph CPX (which latter was pretty awesome actually).
>> 
>> You mean graphical languages? I’m not sure how well they scale. Also
>> there’s the problem that standard source manipulations like diff/patch
>> and merge are lacking.
> 
> Plenty of others have textual source. Oz for example.

So what does the language processor operate on: the graphical presentation, 
or the underlying source?

> Also, I'm not sure what you mean by "scale" in this context.

As in “deal with very complex programs”.

>>> And one can argue that stitching together UNIX tools with tees and pipes
>>> and redirects is a form of dataflow.
>> 
>> That can express fan-out, but I don’t think it can express subsequent
>> fan-in from that fan-out.
> 
> In many cases you could do it in a shell but you'd have to get quite
> creative. I'm not claiming in any case that UNIX system and shell offers
> a full, high-powered dataflow environment.

There is always the option of bringing in named pipes (mkfifo or mknod p) to 
tie disparate ends together.

>> The standard solution to this is to introduce names for the connections
>> somewhere. Which basically brings us right back to functional
>> programming.
> 
> The "names" are single-assignment (logic) dataflow variables, that are
> either unbound or bound. Computations express their dependency on
> inputs; specifying the connections is a matter of indicating that
> certain inputs are outputs of another computation.
> 
> It's not so much that _this_ is functional programming, but more that
> many functional programming languages allow you to program in such a way
> that it resembles dataflow.

Seems like every attempt at dataflow has to resort to using names as an 
alternative to topology at some point.
0
Reply ldo (2144) 3/31/2011 5:50:17 AM

On 11-03-31 02:50 AM, Lawrence D'Oliveiro wrote:
> In message <RCkjp.2728$%f5.828@newsfe08.iad>, Arved Sandstrom wrote:
> 
>> On 11-03-25 11:39 PM, Lawrence D'Oliveiro wrote:
>>
>>> In message <Vv7jp.1732$in3.386@newsfe17.iad>, Arved Sandstrom wrote:
>>>
>>>> If you want to do dataflow then the best way to do it is using a
>>>> dataflow language. I've used a few of them, including LabVIEW and
>>>> Prograph CPX (which latter was pretty awesome actually).
>>>
>>> You mean graphical languages? I’m not sure how well they scale. Also
>>> there’s the problem that standard source manipulations like diff/patch
>>> and merge are lacking.
>>
>> Plenty of others have textual source. Oz for example.
> 
> So what does the language processor operate on: the graphical presentation, 
> or the underlying source?

AFAIK the graphics are just graphics; I can't speak for every graphical
language out there. LabVIEW has used a compiler since version 2.0
(version 1.0 was an interpreter on Motorola 68000). Prograph started out
in its earliest incarnations written in Prolog but moved to C early on -
I wrote new Prograph primitives for the hell of it, for matrix
operations, in C back around 20 years ago.

Generally speaking the primitives in such a graphical language are going
to be compiled bits in a library, and the program is a description of
how they stitch together, as well as storage of the layout information.

Also typically there is no underlying source - what you see *is* the source.

When I said "plenty of others" I meant _non-graphical_ dataflow
languages, incidentally. Like Oz.

>> Also, I'm not sure what you mean by "scale" in this context.
> 
> As in “deal with very complex programs”.

You're not going to have worse problems with a dataflow language than
any other. It depends on the problem. Odds are you'll do better at
managing complexity with dataflow then you will with a general purpose
OOP language, for the multitude of problems that lend themselves to
dataflow.

[ SNIP ]

AHS

-- 
That's not the recollection that I recall...All this information is
certainly in the hands of the auditor and we certainly await his report
to indicate what he deems has occurred.
-- Halifax, Nova Scotia mayor Peter Kelly, who is currently deeply in
the shit
0
Reply asandstrom3minus1 (421) 3/31/2011 11:51:14 PM

43 Replies
139 Views

(page loaded in 0.365 seconds)


Reply: