zsh-users
 help / color / mirror / code / Atom feed
* Local inner functions
@ 2011-03-23 23:23 René 'Necoro' Neumann
  2011-03-24  1:16 ` nix
  2011-03-24  1:41 ` Bart Schaefer
  0 siblings, 2 replies; 6+ messages in thread
From: René 'Necoro' Neumann @ 2011-03-23 23:23 UTC (permalink / raw)
  To: Zsh Users

[-- Attachment #1: Type: text/plain, Size: 515 bytes --]

Hi all,

I often use the construct of defining a function inside another
function. Now I discovered, that these pour into the environment, i.e.
if I have

foo ()
{
   bar () { }
}

and I execute "foo", "bar" is now visible in my environment.

Is there some way of making 'bar' to be local to 'foo'? Using the
'local' keyword does not work :).

I know, that in principle it is possible to explicitly 'unfunction'
functions, but this is not a usable way sometimes.

Thanks for your help,
René


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Local inner functions
  2011-03-23 23:23 Local inner functions René 'Necoro' Neumann
@ 2011-03-24  1:16 ` nix
  2011-03-24  8:50   ` René 'Necoro' Neumann
  2011-03-24  1:41 ` Bart Schaefer
  1 sibling, 1 reply; 6+ messages in thread
From: nix @ 2011-03-24  1:16 UTC (permalink / raw)
  To: "René 'Necoro' Neumann"; +Cc: zsh-users

> Hi all,
>
> I often use the construct of defining a function inside another
> function. Now I discovered, that these pour into the environment, i.e.
> if I have
>
> foo ()
> {
>    bar () { }
> }
>
> and I execute "foo", "bar" is now visible in my environment.
>
> Is there some way of making 'bar' to be local to 'foo'? Using the
> 'local' keyword does not work :).
>
> I know, that in principle it is possible to explicitly 'unfunction'
> functions, but this is not a usable way sometimes.
>
> Thanks for your help,
> René
>
>

I think localing is possibly only to variables as you know. What you're
trying to achieve by localing 'bar' inside 'foo'?




^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Local inner functions
  2011-03-23 23:23 Local inner functions René 'Necoro' Neumann
  2011-03-24  1:16 ` nix
@ 2011-03-24  1:41 ` Bart Schaefer
  2011-03-24  9:38   ` René 'Necoro' Neumann
  1 sibling, 1 reply; 6+ messages in thread
From: Bart Schaefer @ 2011-03-24  1:41 UTC (permalink / raw)
  To: Zsh Users

On Wed, Mar 23, 2011 at 4:23 PM, René 'Necoro' Neumann <lists@necoro.eu> wrote:
>
> foo ()
> {
>   bar () { }
> }
>
> Is there some way of making 'bar' to be local to 'foo'? Using the
> 'local' keyword does not work :).

The short answer is "no."  And you can't make local aliases either.

The slightly longer answer is that there are a couple of ways to fudge
it, of varying degrees of hackishness.

The first and least hackish is to use an "anonymous" function (which,
it has been pointed out elsewhere, are slightly misnamed because
they're not true closures):

foo() {
  local x=foo
  () {
    local x=bar
    print "Look, it's a nested scope: $x"
  }
  print "Back in the function scope: $x"
}

However, this only works when the inner scope needs to be called in
only one place, that is, you don't need to refer to it by name or pass
it arguments.  It's called immediately as soon as it's defined.

Alternately, as you mentioned, you can define your functions and then
unfunction them again.  If you employ an "always" block there
shouldn't be any situation where this doesn't work, but it does mean
you have to avoid name clashes.  The following assumes you setopt
FUNCTION_ARG_ZERO:

foo() {
  { # Begin "always" block
    function $0_bar {
      print "A function scope with a fudged name: $0"
    }
    print "Calling..."
    $0_bar
    print "... back in $0"
  } always {
    unfunction -m "$0_*"
  }
}

Now we reach the real hack.  If you don't care what your function is
named, you can hijack signal trapping functions for signals that it's
very unlikely your script will receive.  Examples are USR1, USR2, URG,
PWR, and SYS, but not all platforms have all those signals so in
practice you probably get only USR1 and USR2.

foo() {
  setopt localoptions localtraps
  TRAPUSR1() {
    print "A handler for a signal that never comes"
  }
  print "Calling..."
  TRAPUSR1
  print "... back in function scope"
}

The localtraps option causes the TRAPUSR1 function to be removed
automatically when the function scope ends.  It also does the right
thing if another function defines the same nested function name and
one of the two outer functions calls the other one.

(So theoretically the plumbing is all there to create local nested
functions, but the devil is in the details, as they say.)


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Local inner functions
  2011-03-24  1:16 ` nix
@ 2011-03-24  8:50   ` René 'Necoro' Neumann
  0 siblings, 0 replies; 6+ messages in thread
From: René 'Necoro' Neumann @ 2011-03-24  8:50 UTC (permalink / raw)
  To: zsh-users

[-- Attachment #1: Type: text/plain, Size: 1113 bytes --]

Am 24.03.2011 02:16, schrieb nix@myproxylists.com:
>> Hi all,
>>
>> I often use the construct of defining a function inside another
>> function. Now I discovered, that these pour into the environment, i.e.
>> if I have
>>
>> foo ()
>> {
>>    bar () { }
>> }
>>
>> and I execute "foo", "bar" is now visible in my environment.
>>
>> Is there some way of making 'bar' to be local to 'foo'? Using the
>> 'local' keyword does not work :).
>>
>> I know, that in principle it is possible to explicitly 'unfunction'
>> functions, but this is not a usable way sometimes.
>>
>> Thanks for your help,
>> René
>>
>>
> 
> I think localing is possibly only to variables as you know. What you're
> trying to achieve by localing 'bar' inside 'foo'?
> 

Avoiding name space cluttering and also avoiding the possibility of
overwriting already existing functions or aliases.

Take for example:

bar () {
	echo muh
}

...

foo () {
	bar () { echo moep }
}

No after executing 'foo', 'bar' does something completely different,
which is in most cases extremely unwelcome.

- René


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Local inner functions
  2011-03-24  1:41 ` Bart Schaefer
@ 2011-03-24  9:38   ` René 'Necoro' Neumann
  2011-03-24 11:51     ` Mikael Magnusson
  0 siblings, 1 reply; 6+ messages in thread
From: René 'Necoro' Neumann @ 2011-03-24  9:38 UTC (permalink / raw)
  To: zsh-users

[-- Attachment #1: Type: text/plain, Size: 854 bytes --]

Am 24.03.2011 02:41, schrieb Bart Schaefer:
> On Wed, Mar 23, 2011 at 4:23 PM, René 'Necoro' Neumann
<lists@necoro.eu> wrote:
>>
>> foo ()
>> {
>>   bar () { }
>> }
>>
>> Is there some way of making 'bar' to be local to 'foo'? Using the
>> 'local' keyword does not work :).
>
> The short answer is "no."  And you can't make local aliases either.
>
> The slightly longer answer is that there are a couple of ways to fudge
> it, of varying degrees of hackishness.

Thanks for your answer. Instead of your 'always'-block, I tried
trap 'unfunction bar' EXIT - but of course this is no good in case of
name clashes :).

So, if nothing like this really works (and local functions are not been
to be implemented into zsh), I'll just use the $0_bar approach (wasn't
aware of this), to at least reduce the chance of clashes.

- René


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Local inner functions
  2011-03-24  9:38   ` René 'Necoro' Neumann
@ 2011-03-24 11:51     ` Mikael Magnusson
  0 siblings, 0 replies; 6+ messages in thread
From: Mikael Magnusson @ 2011-03-24 11:51 UTC (permalink / raw)
  To: René 'Necoro' Neumann; +Cc: zsh-users

On 24 March 2011 10:38, René 'Necoro' Neumann <lists@necoro.eu> wrote:
> Am 24.03.2011 02:41, schrieb Bart Schaefer:
>> On Wed, Mar 23, 2011 at 4:23 PM, René 'Necoro' Neumann
> <lists@necoro.eu> wrote:
>>>
>>> foo ()
>>> {
>>>   bar () { }
>>> }
>>>
>>> Is there some way of making 'bar' to be local to 'foo'? Using the
>>> 'local' keyword does not work :).
>>
>> The short answer is "no."  And you can't make local aliases either.
>>
>> The slightly longer answer is that there are a couple of ways to fudge
>> it, of varying degrees of hackishness.
>
> Thanks for your answer. Instead of your 'always'-block, I tried
> trap 'unfunction bar' EXIT - but of course this is no good in case of
> name clashes :).
>
> So, if nothing like this really works (and local functions are not been
> to be implemented into zsh), I'll just use the $0_bar approach (wasn't
> aware of this), to at least reduce the chance of clashes.

If your function doesn't need to modify the parent environment in any
way, you can simply run it in a subshell:

function bar() { echo stuff }
function foo() {
 (
  function bar() { echo whatever }
  bar
 )
}
foo
bar

-- 
Mikael Magnusson


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2011-03-24 11:59 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-03-23 23:23 Local inner functions René 'Necoro' Neumann
2011-03-24  1:16 ` nix
2011-03-24  8:50   ` René 'Necoro' Neumann
2011-03-24  1:41 ` Bart Schaefer
2011-03-24  9:38   ` René 'Necoro' Neumann
2011-03-24 11:51     ` Mikael Magnusson

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).