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