proc without stack frame OR macro instead of proc 
Author Message
 proc without stack frame OR macro instead of proc

Dear Tcl Gurus,

a Tcl proc is evaluated in itsown stack context. However, there are ways to
influence parent stack contexts with upvar and uplevel.

Question: Is it possible to have [macro] as a counterpart to [proc]
such that [macro] defines a "function" which is evaluated in the
caller's stack context, i.e which does not create its own stack context?

A partially working attempt is this:

proc macro {name params body} {
  proc $name $params "
    set script \[subst -nocommands -nobackslashes {$body}\]
    puts ===\$script===
    uplevel 1 \$script
  "

Quote:
}

You can now do:

  ## Another [set], which assigns the result of a script
  macro xset {var script} {
    puts [eval $script]"
  }
  xset text  concat Hello world.   ;# will set text to "Hello world."

Another example could be

  ## a replacement for return, which also prints the value to be returned
  macro debugReturn {result} {
    puts "[info level 0] --> $result"
    return $result
  }
  proc try {} {
    debugReturn "joe."
  }
  puts "try returned [try]"

However, if you make a bit more elaborate examples, this breaks
horribly. Already

  debugReturn "\[joe\]"

does not work. The reason seems to be that [subst]ed values are put
flat into the script instead of establishing a list element.

Again the question. Can a decent [macro] be done? If it is done once and
for all, I can get rid of a whole lot of unwieldy [uplevel]ing.

  Harald Kirsch



Mon, 12 Jul 2004 05:33:05 GMT  
 proc without stack frame OR macro instead of proc
        ...

Quote:
> a Tcl proc is evaluated in itsown stack context. However, there are ways to
> influence parent stack contexts with upvar and uplevel.

> Question: Is it possible to have [macro] as a counterpart to [proc]
> such that [macro] defines a "function" which is evaluated in the
> caller's stack context, i.e which does not create its own stack context?

I think what you are trying to create is lamda procs.  See:
        http://mini.net/tcl/519.html

--
  Jeff Hobbs                     The Tcl Guy
  Senior Developer               http://www.ActiveState.com/
      Tcl Support and Productivity Solutions



Mon, 12 Jul 2004 14:14:20 GMT  
 proc without stack frame OR macro instead of proc
        ...

Quote:
> a Tcl proc is evaluated in itsown stack context. However, there are ways to
> influence parent stack contexts with upvar and uplevel.

> Question: Is it possible to have [macro] as a counterpart to [proc]
> such that [macro] defines a "function" which is evaluated in the
> caller's stack context, i.e which does not create its own stack context?

> I think what you are trying to create is lamda procs.  See:
>    http://mini.net/tcl/519.html

The routines discussed there just create a proc, the only interesting
thing is how to invent a name for it ;-) But how's this:

 proc macro {name body} {
        proc $name {} [list uplevel 1 $body]
        set name ;# not needed, but maybe helpful
 }
0 % macro now {incr i; incr j}
now
0 % set i 1
1
0 % set j 10
10
0 % now
11
0 % now
12
0 % now
13
0 % set i
4
--
Schoene Gruesse/best regards, Richard Suchenwirth - +49-7531-86 2703
Siemens Dematic AG, PA RC D2, Buecklestr.1-5, 78467 Konstanz,Germany
Personal opinions expressed only unless explicitly stated otherwise.



Mon, 12 Jul 2004 17:11:00 GMT  
 proc without stack frame OR macro instead of proc

Quote:


>    ...
> > a Tcl proc is evaluated in itsown stack context. However, there are ways to
> > influence parent stack contexts with upvar and uplevel.

> > Question: Is it possible to have [macro] as a counterpart to [proc]
> > such that [macro] defines a "function" which is evaluated in the
> > caller's stack context, i.e which does not create its own stack context?

> I think what you are trying to create is lamda procs.  See:
>    http://mini.net/tcl/519.html

Not at all. The lambda discussed there creates a proc which is
executed in its own stack context. I would definitively expect some use
of [uplevel].

  Harald Kirsch
--
----------------+------------------------------------------------------

LION bioscience | +49 6221 4038 172          |        -- Paul Erd?s
       *** Please do not send me copies of your posts. ***



Mon, 12 Jul 2004 20:28:17 GMT  
 proc without stack frame OR macro instead of proc

Quote:


>    ...
> > a Tcl proc is evaluated in itsown stack context. However, there are ways to
> > influence parent stack contexts with upvar and uplevel.

> > Question: Is it possible to have [macro] as a counterpart to [proc]
> > such that [macro] defines a "function" which is evaluated in the
> > caller's stack context, i.e which does not create its own stack context?
> But how's this:

>  proc macro {name body} {
>    proc $name {} [list uplevel 1 $body]
>    set name ;# not needed, but maybe helpful
>  }

I was actually after a version with parameters. The beasty part is the
expansion of the macro's paramters within the macro's stack context
and then run the resulting body as you do here with [uplevel].

I tried it with [subst] as detailed in my original post, but this puts
the expansion all to often into a wrong quoting context.

Finally, a really challenge is to have local variable in
the macro in addition to the parameters.

  Harald Kirsch

--
----------------+------------------------------------------------------

LION bioscience | +49 6221 4038 172          |        -- Paul Erd?s
       *** Please do not send me copies of your posts. ***



Mon, 12 Jul 2004 20:42:49 GMT  
 proc without stack frame OR macro instead of proc

Quote:

> I was actually after a version with parameters. The beasty part is the
> expansion of the macro's paramters within the macro's stack context
> and then run the resulting body as you do here with [uplevel].

> I tried it with [subst] as detailed in my original post, but this puts
> the expansion all to often into a wrong quoting context.

> Finally, a really challenge is to have local variable in
> the macro in addition to the parameters.

You might want to define more cleanly exactly what you mean. It sounds
more like what you're looking for is a routine like upvar except that it
pulls all the variables from the parent's space into your space?

Proc arguments are already evaluated in the caller's space, so I'm not
sure what 'expansion of the macro's parameters withing the marcro's
stack context" is supposed to mean, for example.

--
Darren New
San Diego, CA, USA (PST). Cryptokeys on demand.
  The opposite of always is sometimes.
   The opposite of never is sometimes.



Tue, 13 Jul 2004 01:41:10 GMT  
 proc without stack frame OR macro instead of proc

Quote:


> > I was actually after a version with parameters. The beasty part is the
> > expansion of the macro's paramters within the macro's stack context
> > and then run the resulting body as you do here with [uplevel].

> > I tried it with [subst] as detailed in my original post, but this puts
> > the expansion all to often into a wrong quoting context.

> > Finally, a really challenge is to have local variable in
> > the macro in addition to the parameters.

> You might want to define more cleanly exactly what you mean. It sounds
> more like what you're looking for is a routine like upvar except that it
> pulls all the variables from the parent's space into your space?

No, exactly that is what I don't want.  I want something like [proc],
say [macro], which can define a function. As a base for discussion
consider

  proc  p {a} {
    upvar other my;
    doSomething $a $my;
    puts [info level]
    puts [info vars]
  }
  macro m {x} {
    doSomething $x $other
    puts [info level]
    puts [info vars]
  }

I want both of them to work. But I want crucially different
behaviour the essence of which is described by

  the body of m is evaluated in the caller's stack frame

If you do a simple [uplevel] of the whole body of m, it will not see
the correct value of parameter x.

In particular I want that in the script

  set first 1
  set other 1  
  m 2

macro m's body will see x==2 and other==2. In addition [info level] is
one less than if it were a proc and [info vars]=={x other first}

  Harald

--
----------------+------------------------------------------------------

LION bioscience | +49 6221 4038 172          |        -- Paul Erd?s
       *** Please do not send me copies of your posts. ***



Tue, 13 Jul 2004 16:06:20 GMT  
 proc without stack frame OR macro instead of proc

Quote:

>   proc  p {a} {
>     upvar other my;
>     doSomething $a $my;
>     puts [info level]
>     puts [info vars]
>   }
>   macro m {x} {
>     doSomething $x $other
>     puts [info level]
>     puts [info vars]
>   }

> I want both of them to work. But I want crucially different
> behaviour the essence of which is described by

>   the body of m is evaluated in the caller's stack frame

> If you do a simple [uplevel] of the whole body of m, it will not see
> the correct value of parameter x.

> In particular I want that in the script

>   set first 1
>   set other 1
>   m 2

> macro m's body will see x==2 and other==2. In addition [info level] is
> one less than if it were a proc and [info vars]=={x other first}

Hm, what would happen if you already had a variable x in the calling
proc? Which one would prevail? Say:

   proc Problematic { y z w } {

      set x 2

      m 3
   }

Regards,

Arjen



Tue, 13 Jul 2004 16:32:03 GMT  
 proc without stack frame OR macro instead of proc

Quote:


[snip]
> > If you do a simple [uplevel] of the whole body of m, it will not see
> > the correct value of parameter x.

> > In particular I want that in the script

> >   set first 1
> >   set other 1
> >   m 2

> > macro m's body will see x==2 and other==2. In addition [info level] is
> > one less than if it were a proc and [info vars]=={x other first}

> Hm, what would happen if you already had a variable x in the calling
> proc? Which one would prevail? Say:

>    proc Problematic { y z w } {

>       set x 2

>       m 3
>    }

The 'principle of least surprise' suggests (my opinion) to let the
parameter have preference. It allow similar things as you can do in
Java and C++ where you have

   int i = -101
   for(int i=0; i<10; i++) {...}
   // i is still -101

In Tcl, my macro-idea would probably allow to implement something
similar:

   macro fromto {var from to} body {
     for {set $var $from} {[set $var]<$to} {incr $var} {
        eval $body
     }
   }

could be used like

   set other "Hello"
   set i -101
   fromto {i 0 10} {
     do somthing with $i being different from -101
     but $other=="Hello" as expected
   }
   ## Here, $i is -101 as before

  Harald Kirsch

--
----------------+------------------------------------------------------

LION bioscience | +49 6221 4038 172          |        -- Paul Erd?s
       *** Please do not send me copies of your posts. ***



Tue, 13 Jul 2004 23:22:49 GMT  
 proc without stack frame OR macro instead of proc

Quote:

>   proc  p {a} {
>     upvar other my;
>     doSomething $a $my;
>     puts [info level]
>     puts [info vars]
>   }
>   macro m {x} {
>     doSomething $x $other
>     puts [info level]
>     puts [info vars]
>   }

> I want both of them to work. But I want crucially different
> behaviour the essence of which is described by

>   the body of m is evaluated in the caller's stack frame

> If you do a simple [uplevel] of the whole body of m, it will not see
> the correct value of parameter x.

I think I get what he's driving at: he wants the effect
of an actual macro call.

-A-

Quote:
> set first 1
> set other 1  
> m 2

Should be processed as if it said:

-B-
set first 1
set other 1
doSomething 2 $other

Something like this could be mocked up with an
appropriate procedure, but to do the job right
the macro should only be evaluated once - that
is, -A- should be byte-compiled as if it were
-B-.  Not looking at the code, this should be
a fairly trivial change, when the interp goes
to compile "m 2" it should note that m is a
macro, not a proc, eval it and then byte-compile
the result.  That is:

macro m { x } {
  return "doSomething $x \$other"

Quote:
}

should be run at compile-time and the result
byte-compiled as the representation of "m 2".

--
 .-.    .-. .---. .---. .-..-. | Wild Open Source Inc.
 | |__ / | \| |-< | |-<  >  /  | "Making the bazaar just a
 `----'`-^-'`-'`-'`-'`-' `-'   |  little more commonplace."
  home: www.smith-house.org    | work: www.wildopensource.com



Wed, 14 Jul 2004 01:00:48 GMT  
 proc without stack frame OR macro instead of proc

Quote:

> The 'principle of least surprise' suggests (my opinion) to let the
> parameter have preference. It allow similar things as you can do in
> Java and C++ where you have

So in what way is your "macro" different from a lambda with free
variables?

--
Darren New
San Diego, CA, USA (PST). Cryptokeys on demand.
  The opposite of always is sometimes.
   The opposite of never is sometimes.



Wed, 14 Jul 2004 02:15:53 GMT  
 proc without stack frame OR macro instead of proc

Quote:

> Question: Is it possible to have [macro] as a counterpart to [proc]
> such that [macro] defines a "function" which is evaluated in the
> caller's stack context, i.e which does not create its own stack context?

Thank you for all your answers. I apologize for breaking this interesting
thread because I went on a one-week vacation. As a compensation I offer
at least a version 0.1 of a solution below. Comments and testers are welcome.
Note that examples are at the very end of this longish post.

   Regards,
   Harald Kirsch

namespace eval ::macro {
  namespace export macro

Quote:
}

########################################################################
##
## can be used like `proc', but the function defined tries to behave
## as if it does not create a new stack context.
#
proc ::macro::macro {name params body} {
  set b "
    ::macro::pushParams
    set code \[catch {uplevel 1 {$body}} result\]
    ::macro::popParams
  "
  append b {
    switch $code {
      0 {return $result}
      1 {return -code error -errorinfo $::errorInfo $result}
      2 {return $result}
      3 {return -code error -errorinfo "break not in loop" $result}
      4 {return -code error -errorinfo "continue not in loop" $result}
      default {return -code $code -errorinfo $::errorInfo $result}
    }
  }
  uplevel 1 [list proc $name $params $b]
Quote:
}

########################################################################
##
## given a macro, say child, having params a_1, a_2, ..., is called by
## some function, say `parent', then child calls this function to
## 1) make a backup copy of eventually existing variables visible
## within parent with the names a_1, a_2, etc.
## 2) copy the parameter's values into parent's variables of these
## names.
#
proc ::macro::pushParams {} {
  ## get list of params of calling proc (should be a macro)
  set upparams [info args [lindex [info level -1] 0]]

  ## invent a name which is unique within calling proc and call it
  ## `store' right here
  set storename s[join $upparams ""]
  upvar $storename store

  ## If the proc two levels up the stack has a variable with a name
  ## listed in upparams, we make a copy and save it in `store'. The we
  ## assign to it whatever we find in the parameter of our caller
  ## (typically a macro).
  #puts "storing to $storename"
  foreach p $upparams {
    upvar 2 $p here

    #catch {puts "saving `$p'=$here"}
    ## we store a pair the first element of which is either "a" for
    ## array or "s" for scalar.
    if {[array exist here]} {
      set store($p) [list a [array get here]]
    } elseif {[info exist here]} {
      set store($p) [list s [set here]]
    }

    ## clean up `here' and assign the parameter value
    catch {unset here}
    set here [uplevel 1 set "{$p}"]
  }

Quote:
}

########################################################################
##
## reverts the work of pushParams.
#
proc ::macro::popParams {} {
  ## get hold of the name of the store in our caller (a macro)
  set upparams [info args [lindex [info level -1] 0]]
  set storename s[join $upparams ""]
  upvar $storename store

  ## restore the values two levels up the stack
  #puts "restoring from $storename"
  foreach p [array names store] {
    #catch { puts "restoring `$p'=$store($p)" }
    foreach {tag value} $store($p) break
    upvar 2 $p here
    catch {unset here}
    switch $tag {
      a {array set here $value}
      s {set here $value}
      default {
        error " this cannot happen"
      }
    }
  }

Quote:
}

########################################################################

## some test cases
namespace import ::macro::macro

## nloop iterates from 0 to just below max. Note that body can access
## variables of the caller without the need for upvar.
macro nloop {i max body} {
  for {set i 0} {$i<$max} {incr i} { eval $body }

Quote:
}

## set a variable to the evaluation of some script
macro eset {var args} {
  set $var [eval $args]

Quote:
}

## print the result of evaluating a command
macro eputs {args} {
  puts [eval $args]

Quote:
}

## enclose some text in xml-tags after expanding it, either with eval
## or with subst.
macro etag {tag body} {
  return "<$tag>[eval $body]</$tag>"
Quote:
}

macro stag {tag body} {
  return "<$tag>[subst $body]</$tag>"

Quote:
}

##
## Run this whole script on the command line and give one of the
## switch-tags below as a command line parameter.
##
switch [lindex $argv 0] {
  nloop {
    ## Test it while checking that an existing array named like the
    ## loop variable is only temporarily overwritten
    set i(1) "still there"
    nloop i 5 {puts $i}
    puts $i(1)
  }

  eset {
    set y 4.0
    eset a expr {atan(1.0/$y)*exp(1)}
    puts $a
  }

  tag {
    set short "a shortcut for some longer text"
    eputs etag doc {
      stag p {
        Here comes the document. It can have
        some [stag b bold] too, and of course it may have $short.
      }
    }
  }

Quote:
}



Fri, 23 Jul 2004 03:11:43 GMT  
 
 [ 13 post ] 

 Relevant Pages 

1. setting a stack frame within a PROC

2. Creating procedure frames without proc?

3. TSR, proc near, proc far

4. PROC within a PROC DD override

5. i need my proc-1 to be the same as an existing proc-2

6. Ho do I invoke proc from within proc

7. proc in proc

8. calling a proc from proc

9. Calling a proc within a proc in tcl/tk8.0

10. Control Frame from Proc

11. EXOSPACE PROC DEPTH / STACK

12. preventing the current proc to go on the error stack

 

 
Powered by phpBB® Forum Software