3
$\begingroup$

NB: In this post, by fully qualified names I mean names like Global`x or SomePackage`y, which include their complete context information, and they are to be contrasted with unqualified names, such as x or ArcSin. Also by the current namespace I mean the set of all unqualified names that Mathematica can interpret at any given time during a session. To "pollute the current namespace" means to add unwanted names to it.


For some time now I've been trying (unsuccessfully) to find a way to mimic in Mathematica Python's import functionality.

Specifically, in Python one can gain access to symbols implemented in external modules, without adding those symbols to the current namespace.

For example, suppose some function hello is defined in a module foo.py. Then client code can access this function like this:

import foo
foo.hello()

The crucial point here is that, after import foo, client code must still use the fully qualified name foo.hello. In fact, the following variant bombs:

import foo
hello() # fails with "NameError: name 'hello' is not defined"

(Of course, Python also has a mechanism to add externally defined symbols to the current namespace, but that's another matter.)

My problem with Mathematica is that it seems to be extremely hard to use externally defined symbols without polluting the current namespace.

AFAICT, one cannot import symbols defined within a BeginPackage["..."]/EndPackage[] pair without polluting the current namespace. For example, if the file foo.m contains this:

(* foo.m *)

BeginPackage["foo`"];
hello[] := Print["I'm here!"];
EndPackage[];

...then the following client code succeeds (assuming that foo.m is in $Path):

Needs["foo`"];
hello[]
(* I'm here! *)

(The same thing happens if one uses Get instead of Needs.)

If, on the other hand, I use Begin/End instead of BeginPackage["..."]/EndPackage[], like this:

(* foo.m *)

Begin["foo`"];
hello[] := Print["I'm here!"];
End[];

...then at least, importing this code with Get1, hello can be accessed only as foo`hello:

Get["/tmp/foo.m"];

hello[]
(* hello[] *)

foo`hello[]
(* I'm here! *)

I thought that this was the way achieve what I was after, but today I discovered that this scheme can lead to really bewildering bugs.


So I'm back to square 1.

Is there some other way to import externally defined symbols without making them accessible as unqualified names?


1 This doesn't work so well with Needs.

$\endgroup$
3
  • 1
    $\begingroup$ I have found this topic, with really neat answers. They focus more on creating process than importing but let me know, maybe it is close enough for a duplicate anywa: How to define a package without adding it to $ContextPath $\endgroup$
    – Kuba
    Commented Mar 10, 2017 at 6:54
  • 1
    $\begingroup$ p.s. daily tip how to find those answers, go to users - find Leonid - click - add Block $ContextPath to query. Done ;) $\endgroup$
    – Kuba
    Commented Mar 10, 2017 at 6:55
  • $\begingroup$ @Kuba: Thank you for helping me formulate the right question, and for your answers and comments. I now have a lot to work with. $\endgroup$
    – kjo
    Commented Mar 10, 2017 at 11:33

2 Answers 2

5
$\begingroup$

One way to achieve this is to add the package with Needs and then remove the package context from the context path, $ContextPath. An example with "DatabaseLink`" follows.

Needs["DatabaseLink`"]
SQLConnections[]
{}

"DatabaseLink`" is loaded and its functions are available in the current context. Remove "DatabaseLink`" from $ContextPath.

$ContextPath = $ContextPath /. "DatabaseLink`" -> Nothing;

Now its functions will not evaluate directly.

SQLConnections[]
SQLConnections[]

However, if qualified with the context they do evaluate.

DatabaseLink`SQLConnections[]
{}

Let me say that I do not know if this has any sort of knock-on effect in your context. I think it should be innocent enough but I would defer to more seasoned and knowledgeable members of this forum.

Hope this helps.

$\endgroup$
5
$\begingroup$

Write packages as always, with BeginPackage + Begin and in order to import them your way do what Edmund did or:

Internal`InheritedBlock[
    {$ContextPath}
  , << GeneralUtilities`
]

Block should be enough but this is a more general in case of some fancy package setup.

? PrintDefinitions

? GeneralUtilities`PrintDefinitions

enter image description here

This answers the question of how to import a package to not introduce new contexts/symbols on $ContextPath.

If you want to know how to write the package which will do this no matter how it is loaded then still use BeginPackage+Begin setup but add Edmund's fix just after EndPackage[].

$\endgroup$

Your Answer

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

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