[comp.lang.icon] Multiple value assignment in icon???

bdb@cl.cam.ac.uk (Brian Brunswick) (03/14/91)

I am fairly new to icon, but am considering thr pros and cons of
writing a medium size project using it.  I came up with a language feature
that I have found useful in the past, namely returning a fixed small
number of distinct values from a procedure and assigning them to separate
variables, and wondered what the particular idiom might be.

That was where the trouble began.

Something like every (x|y):=f() is no good, of course.

I fiddled about, and eventually came up with using this:

    every z:=:y:=:x:=:r123()

where r123 would do

    suspend ![1|2|3]

and ends up with x=1, y=2, z=3

Note the reversed order of xyz.

Now this is a horrible cludge, it seems to me. Am I missing something,
or does this irritation spoil an otherwise pretty language?
Brian.Brunswick@uk.ac.cam.cl  Disclaimer.  Short sig rules!

goer@quads.uchicago.edu (Richard L. Goerwitz) (03/15/91)

Bdb@cl.cam.ac.uk (Brian Brunswick) writes (with regard to the problem
of assigning a series of results to a series of variables):
>
>Something like every (x|y):=f() is no good, of course.
>

This is sooo close to working.  Remember that assignment works
by evaluating expr1, then evaluating expr2, then assigning the
result of expr2 to the result of expr1.  Above we have two ex-
pressions,

    1) (x|y)
    2) f()

You want to assign each value suspended by f() to a succession of
variables.  The trouble is that (x|y) is evaluated first, pro-
ducing x, then f() is evaluated.  If f() succeeds, its result is
assigned to x.  End backtracking.

If you add an every, creating

    every (x|y) := f()

you'll get expr1 producing x, as before, but then you'll get f()
producing every result it can, assigning each in turn to x.  When
it finally fails, x will hold the last result produced by f().
Then (x|y) will be resumed, producing y.  The same thing will
happen to y, namely it will be assigned the value of each result
produced by f() until f() fails, leaving y holding its last pro-
duced value.  Both x and y will end up with the same value - the
last result produced by f() (unless naughty side-effects are in-
volved).

What we really want is to pop off a result from expr1, and then
pop a result off of expr2, assign the result of expr2 to expr1,
then pop another result off of expr1, etc.  Essentially, we are
evaluating expr1 and expr2 in parallel.  Interleaving them.

How do we do this??

Aha!  We've suddenly run into one very good use for coexpressions.
Here's what to do:

    val := create f()
    every (x|y) := @val

Now here's an excercize to see if you fully grasp how Icon de-
references variables.  Why will the following NOT work the way you
want it to?

    var := create (x|y)
    every val := f() do
        var := val


Referring to a solution offered (but not quoted here):

> Am I missing something, or does this irritation spoil an otherwise
> pretty language?

Your solution isn't particularly idiomatic.  You clearly sensed this.
Hence your posting.  Your question was really very good, though, be-
cause you've clearly grasped precisely the sort of situation that makes
coexpressions useful and elegant additions to the language.  You aren't
missing a thing, but rather demonstrating an understanding of the sort
of logic that led to the implementation of coexpressions....

I hope this helps.

-Richard Goerwitz (goer@sophist.uchicago.edu)

bdb@cl.cam.ac.uk (Brian Brunswick) (03/16/91)

In article <1991Mar15.013415.1499@midway.uchicago.edu> goer@quads.uchicago.edu (Richard L. Goerwitz) writes:
>Bdb@cl.cam.ac.uk (Brian Brunswick) writes (with regard to the problem
>of assigning a series of results to a series of variables):
>>
>>Something like every (x|y):=f() is no good, of course.
>>
>
>This is sooo close to working.... [explanation of what goes wrong]
>
>Aha!  We've suddenly run into one very good use for coexpressions.
>Here's what to do:
>
>    val := create f()
>    every (x|y) := @val

Yup, I'd spotted that this was possible, but was put off by the extra
temporary variable needed - I'd really like something on one line.
One can't even do things like ...@(val := create f()), since rhs
evaluated multiple times when lhs is resumed. I did experiment with
putting inteligence in a wrapper function, but it doesn't have enough
information to work safely enough to withstand mistakes.

>Now here's an excercize to see if you fully grasp how Icon de-
>references variables.  Why will the following NOT work the way you
>want it to?
>
>    var := create (x|y)
>    every val := f() do
>        var := val

Urble ... create makes its own copy of local variables ... but would
they even survive undereferenced (ug!) to be (pointlessly) used?  I
suppose globals would work.

>Referring to a solution offered (but not quoted here):

Which was using a wrapper of ![...] around the procedure return values
to turn them into anonymous variables, which enables the calling line
to use a multiple swapping assignment to do rotation amongst several
variables and accumulate results that way. ( every z:=:y:=:x:=:f() )

>
>> Am I missing something, or does this irritation spoil an otherwise
>> pretty language?
>
>Your solution isn't particularly idiomatic.  You clearly sensed this.
>Hence your posting.  Your question was really very good, though, be-
>cause you've clearly grasped precisely the sort of situation that makes
>coexpressions useful and elegant additions to the language.  You aren't
>missing a thing, but rather demonstrating an understanding of the sort
>of logic that led to the implementation of coexpressions....
>
>I hope this helps.
>
>-Richard Goerwitz (goer@sophist.uchicago.edu)

Hmm... I'm not so sure that I don't prefer my cludge to having to
introduce an extra intermediate variable. Of course, its only good so
long as its clearly recognised as an idiom by the reader, otherwise
its needless obfuscation.

Also, I hesitated somewhat at creating a co expression just to do
something that short that I'm likely to use quite a lot. Isn't that
likely to be quite expensive in terms of garbage produced? Or would
reusing the same temporary to hold it mean that reference counting or
something rescues things?
Brian.Brunswick@uk.ac.cam.cl  Disclaimer.  Short sig rules!

goer@ellis.uchicago.edu (Richard L. Goerwitz) (03/18/91)

Bdb@cl.cam.ac.uk (Brian Brunswick) writes:
>>
>>    val := create f()
>>    every (x|y) := @val
>
>Yup, I'd spotted that this was possible, but was put off by the extra
>temporary variable needed - I'd really like something on one line....

You know, I've never understood peoples' resistence to side-effects of
this kind in Icon.  All your garbage gets collected for you, and you are
not going to have any problems with pointers.  The variables, if declared
explicitly local, aren't going to conflict with anything else.  If they
make the code clear and idiomatic, then my own vote is to use them!

>Hmm... I'm not so sure that I don't prefer my cludge to having to
>introduce an extra intermediate variable. Of course, its only good so
>long as its clearly recognised as an idiom by the reader, otherwise
>its needless obfuscation.

I guess that's what I was trying to say.

>Also, I hesitated somewhat at creating a co-expression just to do
>something that short that I'm likely to use quite a lot. Isn't that
>likely to be quite expensive in terms of garbage produced? Or would
>reusing the same temporary to hold it mean that reference counting or
>something rescues things?

You know, I really don't know.  Co-expressions involve less overhead
than a procedure call, as I understand them, and are a bit faster.  If
storage and garbage collection is a problem, you could try writing two
versions of the program, and then check out the IPL program empg.icn.
It's a tool that's ideal for just this kind of profiling.

You could also write a little shell script to turn on memory monitoring,
and then use the IPL routine memsum to get a summary of the results.
I'm thinking of, say,


#!/bin/sh
if
    test $# = 0
then
    echo 'usage:  memmon icon-program [arguments]'
    exit 1
else
    export MEMMON
    MEMMON=tablc.mon
    $*
#   unset MEMMON
    /usr/local/bin/memsum < tablc.mon > tablc.sum
    /bin/cat tablc.sum | egrep -v '0.000$' 1>&2
    /bin/rm tablc.mon tablc.sum
fi


I'll be curious to hear what you eventually settle on.

-Richard (goer@sophist.uchicago.edu)

cjeffery@CS.ARIZONA.EDU (Clinton Jeffery) (03/19/91)

>>Also, I hesitated somewhat at creating a co-expression just to do
>>something that short that I'm likely to use quite a lot. Isn't that
>>likely to be quite expensive in terms of garbage produced? Or would
>>reusing the same temporary to hold it mean that reference counting or
>>something rescues things?

>You know, I really don't know.  Co-expressions involve less overhead
>than a procedure call, as I understand them, and are a bit faster.

Basically everyone is right.
Co-expression >activation< involves less overhead and is a bit faster
than procedure call.  Co-expression >creation< does involve some work
setting up stacks and copying variables.  In many of these situations
there is some magic "average number of results" beyond which co-expressions
are cost-effective.  But of course, Icon is focused on programmer
time more than on execution time.  If you are a busy person and you
are in the habit of avoiding co-expressions for performance reasons,
not only will you miss out on a lot of fun, you will spend a lot more
time writing and debugging, and often you will not gain enough to
have justified it.

Clint

P.S. I have been guilty of avoiding co-expressions before!  Its very
understandable and really is justified, AFTER the program is finished
and you are tuning it.  Why not write it in two lines with a co-expression
first, and write yourself a comment to look at it again later...