Refactoring discovery

R

Roedy Green

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.
 
L

Lawrence D'Oliveiro

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.
 
L

Lawrence D'Oliveiro

Leif Roar said:
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.
 
L

Lew

Roedy said:
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.
 
E

Eric Sosman

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.
 
L

Lawrence D'Oliveiro

Leif Roar said:
(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?
 
L

Lawrence D'Oliveiro

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.
 
J

Joshua Cranmer

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.
 
M

markspace

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?
 
A

Arved Sandstrom

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
 
L

Lawrence D'Oliveiro

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.
 
L

Lawrence D'Oliveiro

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.
 
R

Roedy Green

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.
 
A

Arved Sandstrom

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.
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
 
T

tm

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.
 
L

Lew

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.
 
M

markspace

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.
 
S

Stefan Ram

Eric Sosman said:
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
 
S

Stefan Ram

Leif Roar Moldskred said:
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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top