none
[PS][Scopes] Escaping Parent scopes RRS feed

  • Question

  • Hello fellow scripters,

    What I am trying to do
    once again I am committing the folly of trying to mess with scopes. Specifically, I'm trying to have a function to call the return command of the calling function (and thus end both of them). I've spent quite a few hours on this by now, crawling through the Powershell system without much luck.

    Why I want to do it
    I'm trying to extend my advanced debugging system. Common users will not encounter this, it will not happen without notification, thus it will not affect the rule of don't mess with expected results. Basically I want a unified system to set exit points in functions without having an epic stream of if (Whatever){Write-Warning "WhateverWarning"; return}.

    So yes, it's not causing much of a problem, but it would enable me to write that much neater script.

    What I have tried so far
    I've been trying out quite a few things so far:

    Test 1: Calling the nested function in the same scope as the parent function

    function Get-TestA
    {
    	<#
    		Will write:
    		Test1
    		Test2
    		Test2	
    	#>
    	function Get-TestB
    	{
    		$test = "Test2"
    		Write-Host $test
    		
    		return
    	}
    	
    	$test = "Test1"
    	Write-Host $test
    	. Get-TestB
    	Write-Host $test
    }
    Get-TestA

    This will change the $test variable in the Scope of Get-TestA as intended, but the return call does not affect the parent function (didn't expect it to, but had to verify it, just to be sure).

    Test 2: Invoking the Expression

    function Get-TestA
    {
    	<#
    		Will write:
    		Test1
    		Test2
    		Test1
    		Test1	
    	#>
    	function Get-TestB
    	{
    		$test = "Test2"
    		Write-Host $test
    		Write-Host (Get-Variable "test" -Scope 1).Value
    		
    		return "return"
    	}
    	
    	$test = "Test1"
    	Write-Host $test
    	. IEX (Get-TestB)
    	Write-Host $test
    }
    Get-TestA

    Alas, it did not work either. However, playing around with variables in parent scopes as you can see. Might they prove an answer? Let's see ...

    Test 3: Specifying a sessionstate

    function Get-TestA
    {
    	<#
    		Will write:
    		Test1
    		Test1	
    	#>
    	[CmdletBinding()]
    	Param ()
    	
    	function Get-TestB
    	{
    		[CmdletBinding()]
    		Param (
    			$State = $PSCmdlet.SessionState
    		)
    		$PSCmdlet.InvokeCommand.InvokeScript($State, { return }, $null)
    		
    		
    		return
    	}
    	
    	$test = "Test1"
    	Write-Host $test
    	Get-TestB
    	Write-Host $test
    }
    Get-TestA
    

    alas, they would not. At least not when using the parent SessionState.
    I also tried calling the executioncontext (and its methods) from the parent scope, without success.

    Anybody got any insights in how to realize this?

    Thanks in advance,
    Fred

    Ps.: On break and exit.
    Just in case: Yes, I have thought about using break or exit. Exit will terminate everything, which is not good from a debugging perspective.

    Break is slightly less invasive, as it will - if used unchecked - break all running commands (so if Function A calls Function B which then calls Function C, and function C finally calls "break" it will break out of C, B and A, even if I only want to break out of C and B).

    Using named loops and break works perfectly. Example:

    function TestA
    {
    	<#
    		Writes:
    		I should appear A
    	#>
    	
    	:test while ($true)
    	{
    		TestB
    	}
    	Write-Host "I should appear A"
    }
    
    function TestB
    {
    	TestC
    	Write-Host "I should not appear B"
    }
    
    function TestC
    {
    	break "test"
    	Write-Host "I should not appear C"
    }
    TestA
    

    this will work perfectly. However, it would require me to wrap all interuptible functions into a named loop. This is not always easily feasible and would probably require more effort (and impact readability more) than going with the if-conditional approach.


    There's no place like 127.0.0.1

    Tuesday, July 22, 2014 12:23 PM

Answers

  • The only thing I can think of that would allow you to "abort" a parent function is to throw an exception.

    • Marked as answer by FWN Tuesday, July 22, 2014 2:09 PM
    Tuesday, July 22, 2014 1:30 PM

All replies

  • You cannot do what you are asking.  A scope is isolated.  A scope at a higher level of embeddedness can not see the contents of a called or child function.  A child function can return a value or it can alter the value of a parent variable but cannot be called and change values that are not exposed as parameters.

    This is basic computer design and basic program function. If you go back to programming 101 and redo the parts explain scope I think you will see what is happening.

    Your statement: "Specifically, I'm trying to have a function to call the return command..." makes no sense.  Either you are using the language wrong or are misunderstanding how a return statement works.  You cannot set the return value except by design.  The called function is 100% in charge of its return value.  No outside function or program can alter this reality.

    Go back and rethink what you are asking.  Ask if it actually makes sense.  Ask if you are actually asking the question you think you are asking. 

    Why do you want to globally effect all?

    You can set watch points at any specific line, function or variable and have the debugger intercept but his requires running under the debugger.

    You cannot create or modify the debugger.  The PowerShell debugger is external to the script system,  It is not available for change.  THe nuts and bolts of the debugger are part of the operating system.


    ¯\_(ツ)_/¯

    Tuesday, July 22, 2014 12:51 PM
  • The only thing I can think of that would allow you to "abort" a parent function is to throw an exception.

    • Marked as answer by FWN Tuesday, July 22, 2014 2:09 PM
    Tuesday, July 22, 2014 1:30 PM
  • Hi jrv,

    another charming reply of yours :)

    I'll admit to two linguistic imprecisions:

    1) The terminology in the header is the correct one, not the one in the description: I do not care about the specific method of how to have function C stop function B from working, so long as it's all function C internal and does not affect function A. The only methods (not using the programmatic term here) I knew about that allow me to escape a function (and I'm not going to use a dedicated debugger. Those absolutely won't serve my purpose) are return, break and exit.
    2) Calling return a command. I use that term generically for things that make stuff do things, including statements, functions, cmdlets, methods and kindly asking our receptionist to bring me a cup of coffee. Yeah, I should work on being more precise, at least in the forums.

    Why do you want to globally effect all?
    Cause having the various debugging methods at hand in production code and environment makes it so much easier to debug an exceptional scenario in situ as it occurs. A lab environment can only catch so much after all. This would be just another tool in that collection, but a handy one.
    Oh, and before you present that assumption: I do not replace the testing phase with a presumed better debugging system. I strongly believe in the Demon Murphey, so I want to have both.

    Cheers,
    Fred


    There's no place like 127.0.0.1

    Tuesday, July 22, 2014 1:42 PM
  • The only thing I can think of that would allow you to "abort" a parent function is to throw an exception.

    Hi David,

    thanks for reminding me of Exceptions (How'd I managed to forget about those, when I'm using them day in, day out?). Definitely worth spending some consideration and experimentation on. Not quite the way I'd really prefer to have it work, but definitely beats using break by a fair margin. Probably more elegant then using if-clauses too.

    Cheers and thanks,
    Fred


    There's no place like 127.0.0.1


    • Edited by FWN Tuesday, July 22, 2014 1:50 PM
    Tuesday, July 22, 2014 1:49 PM
  • Let's ignore Irish proverbs for now. What I posted stands.  What you are trying to do cannot be done as you are descripting this.  You cannot inject code into a called function.  Think about how a computer works.  Think about how code is executed.  When a child function is executing the parent virtually doesn't exist.  It has no way of affecting the called function once the function is called.  You can call with parameters and then you are done.  You can set in scope variables and they can be acted on in the called function.  Everything else has to be written in code in the called function.

    This is just simple programming 101.

    You are asking a very vague "what if" kind of question.  I am suggesting that some study of PowerShell, programming technology, and thinking more completely about what you ask will lead you to an understanding of what is wrong with you assumptions about how code and computers work.

    There is one option.  If you can devise a multi-threaded application where you can send messages to the threads you can synthesize what you ask for.  We can do this with "tread local storage", semaphores and other types of messaging events such as message queues.   Doing this in PowerShell would be very difficult for a non-programmer.


    ¯\_(ツ)_/¯

    Tuesday, July 22, 2014 1:54 PM
  • If what David answered is what you are asking then I guess I cannot comprehend the long laundry list of assumptions and discussions. Note that an exception will get you out of everything to the next upper exception handler or out of the script. You still have to design you exception strategy.Suggestion: Try asking simple questions and add detail when asked. Don't try to explain every nuance. It only confuses the question.

    Also "Escaping Parent scopes" seems convoluted.  Perhaps exiting back to the parent for a reason. (Exception) - Hats off to David for decoding that one.


    ¯\_(ツ)_/¯

    Tuesday, July 22, 2014 1:58 PM
  • Hi jrv,

    funny thing. My issue wasn't about injecting code into a called function. It was about a practical way of closing both the current and the parent scope and return to the grandparent one.

    I'll probably be using Exceptions for that (Function A will have to handle that, but it's have to handle all exceptions from B anyway).

    Fred


    There's no place like 127.0.0.1

    Tuesday, July 22, 2014 2:08 PM
  • Hi jrv,

    funny thing. My issue wasn't about injecting code into a called function. It was about a practical way of closing both the current and the parent scope and return to the grandparent one.

    I'll probably be using Exceptions for that (Function A will have to handle that, but it's have to handle all exceptions from B anyway).

    Fred


    There's no place like 127.0.0.1

    Why didn't you just ask that at first.  Forget about using terms like "scope"  You are using it incorrectly.  You want to exit the current function to the parent.  It is your only choice.  There is no automatic way to say I want to exit to any place other than the caller.  If the caller does not react to the return conditions either through a shared variable, return value or exception state then the execution will stay in the paren.  If it is an exception and the caller does not handle the exception the exception will propagate up through the "stack" until it finds and exception handler or exits the script. There are no other options in programming other than those presented here and above.


    ¯\_(ツ)_/¯

    Tuesday, July 22, 2014 2:14 PM