4
$\begingroup$

I want to be able to Get a .wl file. Run some functions (who will do their job working on some separate files that are being written away). And then return to some clean slate so that I can repeat this process with another .wl file and some other functions. This is close to using Quit between these two actions but I want

  1. To keep some basic definitions or at worst automatically redefine them after we obtained a new empty state.
  2. To be able to have multiple of these inputs in one notebook and have it be runable as a whole.

Quit would fail at both these things. My solution for now is to define the basic definitions, protect them and then use functions such as

run[wlfile] := Module[{},
 Quiet@ClearAll["Global`*"];
 Get[(fileName[wlfile])];
 ComputationsUsingDefinitionsInWLFile[a][b][c];
 Quiet@ClearAll["Global`*"];
]

Here a,b,c would be set globally and protected as would fileName[file_] (defined as a function that points to the correct file) and run.

Is there some scoping construct that would do a better job at this?

(I don't know the exact symbols that will be affected by the .wl file (and there will be many) so I can't specifically Block them.)


Ideally the solutions can be used in the following form

throwAwayEnvironment[codeToRun,getCode]

where the first argument contains the code to be run after performing the getCode which Gets definitions (which could be necessary for codeToRun ). The answer by Carl seemed to be able to be written in this form, but I ran into trouble due to the fact that any function called inside Internal ` WithLocalSettings got saved to the wrong environment and did not get removed afterwards. First calling all functions as Symbol["f"] inside WithLocalSettings solves this. However, I then need to automate the finding of functions inside codeToRun and run Symbol["function"] for all of them and all of that without the function first being saved as Global variables. This last seems to be impossible since this is done instantly. Note that if you use Symbol[ToString[f]], this already no longer works since f will already have been made into a global variable.

$\endgroup$

1 Answer 1

9
$\begingroup$

You can block $Context and then read your file:

Block[{$Context="ThrowawayContext`"}, 
    Internal`WithLocalSettings[
        Null,

        Get[(fileName[wlfile])];
        ComputationsUsingDefinitionsInWLFile[a][b][c],

        Remove["ThrowawayContext`*"]
    ]
]

All new symbols will have the context "ThrowawayContext`", and the Internal`WithLocalSettings third argument makes sure that the new symbols are always removed (the third argument is abort protected).

Update

I don't know that you can create a function that automatically parses new symbols into a given context. On the other hand, you can make a Cell style that does this. An example Cell object that meets your requirements:

Cell[
    BoxData[""],
    "Input",
    CellContext -> Cell,
    CellProlog :> ($ContextPath = {"Global`", "System`", $Context}),
    CellEpilog :> Remove["`*"],
    Background -> GrayLevel[.9]
]

The key ingredients are:

  1. CellContext -> Cell

This causes new symbols to be created in a unique context

  1. CellProlog :> ($ContextPath = {"Global`", "System`", $Context})

With the above $ContextPath, new symbols will only be created when they are not present in the "Global`" or "System`" context.

  1. CellEpilog :> Remove["`*"]

The above epilog removes all symbols in the current context, namely the new symbols that have been created.

The easiest way to be able to make use of the above is to add a "Throwaway" style to the style sheet that inherits from the "Input" style. Here is a function that adds the above style to a notebook's style sheet:

SetOptions[
    EvaluationNotebook[],
    StyleDefinitions -> Notebook[
        {
        Cell[StyleData[StyleDefinitions->"Default.nb"]],
        Cell[StyleData["Throwaway", StyleDefinitions->StyleData["Input"]],
            StyleKeyMapping->{"Tab"->"Input"},
            CellContext->Cell,
            CellProlog:>($ContextPath={"Global`",$Context,"System`"}),
            CellEpilog:>Quiet[Remove["`*"]],
            Background->GrayLevel[0.9]
        ],
        Cell[StyleData["Input"],
            StyleKeyMapping->{"Tab"->"Throwaway"}
        ]
        },
        StyleDefinitions->"PrivateStylesheetFormatting.nb"
    ]
]

After evaluating the above, you can create a new "Input" cell, and then use Tab at the start of the cell to convert it to a "Throwaway" cell (this is the point of the StyleKeyMapping options). The background will change to a light gray indicating that you now have a "Throwaway" cell. Then enter your code, and evaluate. For example, with a normal "Input" cell:

NameQ["a"]

False

So, there is no symbol a. Then, create a "Throwaway" cell, and enter the following:

Get[StringToStream["a[i_] := Print[\"test\"]"]]
Print[DownValues[a]]
a[2]

{HoldPattern[a[i_]]:>Print[test]}

test

Finally, check that "a" has not been added to the global name space:

NameQ["a"]

False

$\endgroup$
10
  • $\begingroup$ Thank you! This is almost what I want. Only there is still something happening that I'm confused about. Say I replace ComputationsUsingDefinitionsInWLFile with a function that can be found in the WL file (or some function that calls multiple of these). Than this (those) functions will not be removed (the further ones it calls within the WL file do seem to be removed). What is happening and how can I avoid this? $\endgroup$
    – Kvothe
    Apr 13, 2018 at 15:51
  • $\begingroup$ @Kvothe If I understand correctly, I think your function acquires the Global` context while the input is parsed. To avoid this, use ThrowawayContext`function instead of function. $\endgroup$
    – Carl Woll
    Apr 13, 2018 at 16:40
  • $\begingroup$ Great! Thank you! One last (unimportant) question: is there any way to write this last as something like \$Context `function, I mean using the fact that we already defined \$Context inside the block. (for example ToExpression[\$Context] doesn't work). $\endgroup$
    – Kvothe
    Apr 13, 2018 at 17:02
  • $\begingroup$ @Kvothe You could use Symbol["function"], which will create the symbol function in the current context if it doesn't already exist on the context path. $\endgroup$
    – Carl Woll
    Apr 13, 2018 at 17:14
  • $\begingroup$ Your solutions works in that case. However, in practice it is not really workable. The problem is that I want to be able to use this as a scoping environment function like Module and Block. However, I would need to use Symbol["f"] for any function that gets used inside it. Moreover, I can't even use Symbol[ToString[f]] since this seems to already introduce f in the wrong context before it is converted to a string creating the problem I had before. This means that all functions would need to be written by hand in string form and eliminates all hope of some automation. $\endgroup$
    – Kvothe
    Apr 16, 2018 at 14:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.