13

I have a directory with some helper functions that should be put into a package. Step one is obviously naming the directory something like +mypackage\ so I can call functions with mypackage.somefunction. The problem is, some functions depend on one another, and apparently MATLAB requires package functions to call functions in the very same package still by explicitly stating the package name, so I'd have to rewrite all function calls. Even worse, should I decide to rename the package, all function calls would have to be rewritten as well. These functions don't even work correctly anymore when I cd into the directory as soon as its name starts with a +.

Is there an easier solution than rewriting a lot? Or at least something self-referential like import this.* to facilitate future package renaming?


edit I noticed the same goes for classes and static methods, which is why I put the self-referential part into this separate question.

Community
  • 1
  • 1
Tobias Kienzler
  • 25,759
  • 22
  • 127
  • 221
  • Calling *import* at the beginning of your file should do the job. Check the documentation [import](http://www.mathworks.de/help/techdoc/ref/import.html) for further information. – zellus Apr 12 '11 at 13:10
  • @zellus: thanks, I already tried that - I'd have to include the `import mypackage.*` to _every_ function in the package (imports are in function workspace, not global), and change it when I decide to rename the package :( – Tobias Kienzler Apr 12 '11 at 13:16

2 Answers2

15

In truth, I don't know that you should really be renaming your packages often. It seems to me that the whole idea behind a package in MATLAB is to organize a set of related functions and classes into a single collection that you could easily use or distribute as a "toolbox" without having to worry about name collisions.

As such, placing functions and classes into packages is like a final step that you perform to make a nice polished collection of tools, so you really shouldn't have much reason to rename your packages. Furthermore, you should only have to go through once prepending the package name to package function calls.

... (pausing to think if what I'm about to suggest is a good idea ;) ) ...

However, if you really want to avoid having to go through your package and prepend your function calls with a new package name, one approach would be to use the function mfilename to get the full file path for the currently running package function, parse the path string to find the parent package directories (which start with "+"), then pass the result to the import function to import the parent packages. You could even place these steps in a separate function packagename (requiring that you also use the function evalin):

function name = packagename

  % Get full path of calling function:
  callerPath = evalin('caller', 'mfilename(''fullpath'')');

  % Parse the path string to get package directories:
  name = regexp(callerPath, '\+(\w)+', 'tokens');

  % Format the output:
  name = strcat([name{:}], [repmat({'.'}, 1, numel(name)-1) {''}]);
  name = [name{:}];

end

And you could then place this at the very beginning of your package functions to automatically have them include their parent package namespace:

import([packagename '.*']);

Is this a good idea? Well, I'm not sure what the computational impacts will be if you're doing this every time you call a package function. Also, if you have packages nested within packages you will get output from packagename that looks like this:

'mainpack.subpack.subsubpack'

And the call to import will only include the immediate parent package subsubpack. If you also want to include the other parent packages, you would have to sequentially remove the last package from the above string and import the remainder of the string.

In short, this isn't a very clean solution, but it is possible to make your package a little easier to rename in this way. However, I would still suggest that it's better to view the creation of a package as a final step in the process of creating a core set of tools, in which case renaming should be an unlikely scenario and prepending package function calls with the package name would only have to be done once.

gnovice
  • 125,304
  • 15
  • 256
  • 359
  • thanks, that's the kind of answer I feared would be the best :-7 but maybe I got something wrong conceptually: what would you do if you wrote (or received, to be more accurate) many functions in one directory and now want to turn that into a package (or toolbox, would that make a difference?)? Or is sticking with the package name and just using an `import` in each file a really good solution? – Tobias Kienzler Apr 12 '11 at 16:01
  • 2
    @Tobias: If I were in such a situation, I would first settle on a package name (e.g. `mypack`), then bite the bullet and just go through all the code to find where package functions call each other, prepending `mypack.` to the calls to correctly scope them to the package. This step could be made easier by making use of some dependency tools that tell you which functions call or are called by which other functions. [This SO question](http://stackoverflow.com/questions/5518200/automatically-generating-a-diagram-of-function-calls-in-matlab) links to a few such options. – gnovice Apr 12 '11 at 16:16
  • thanks, that's probably better than heuristically adding `import mypack.*` in every function – Tobias Kienzler Apr 12 '11 at 18:21
  • 1
    [exportToZip](http://blogs.mathworks.com/pick/2009/09/18/easier-and-less-error-prone-creation-of-zip-files/) is also useful to find dependencies – Tobias Kienzler Apr 13 '11 at 07:54
  • [here](http://stackoverflow.com/a/9100724/321973)'s an interesting approach for another `import this.*` solution – Tobias Kienzler Mar 07 '12 at 11:04
  • Do packages still work in this clumsy way? (I’ve never written a package with a `+` directory.) Can’t package functions call other functions in the same package without qualifiers? Seems a poor choice! – Cris Luengo Oct 30 '18 at 13:37
  • 1
    @CrisLuengo: Unfortunately, it appears they do still work this way. – gnovice Oct 30 '18 at 14:23
  • See [this question I had earlier](https://stackoverflow.com/q/52865913/1085062): I haven't tested your solution above, but I suspect `evalin` calling `mfilename` is not going to work here... – Rody Oldenhuis Oct 30 '18 at 19:15
3

I have been exploring answers to the same question and I have found that combining package with private folders can allow most or all of the code to be used without modification.

Say you have

+mypackage\intfc1.m
+mypackage\intfc2.m
+mypackage\private\foo1.m
+mypackage\private\foo2.m
+mypackage\private\foo3.m

Then from intfc1, foo1, foo2, and foo3 are all reachable without any package qualifiers or import statements, and foo1, foo2, and foo3 can also call each other without any package qualifiers or import statements. If foo1, foo2, or foo3 needs to call intfc1 or intfc2, then that needs qualification as mypackage.intfc1 or an import statement.

In the case that you have a large set of mutually interdependent functions and a small number of entry points, this reduces the burden of adding qualifiers or import statements.

To go even further, you could create new wrapper functions at the package level with the same name as private functions

+mypackage\foo1.m          <--- new interface layer wraps private foo1
+mypackage\private\foo1.m  <--- original function

where for example +mypackage\foo1.m might be:

function answer = foo1(some_parameter)
    answer = foo1(some_parameter);  % calls private function, not itself
end

This way, unlike the intfc1 example above, all the private code can run without modification. In particular there is no need for package qualifiers when calling any other function, regardless of whether it is exposed by a wrapper at the package level.

With this configuration, all functions including the package-level wrappers are oblivious of the package name, so renaming the package is nothing more than renaming the folder.

Vector
  • 171
  • 3
  • 1
    The second one is a very interesting approach especially when you're wrapping code from other people and want to use only a couple of functions. Neat! – Elmar Zander Dec 18 '15 at 12:28
  • How would you apply this second approach to classes? – hasdrubal Aug 19 '16 at 13:18
  • @Vector `Then from intfc1, foo1, foo2, and foo3 are all reachable without any package qualifiers or import statements` is not really true. You have to specify the package name or import the package. As mentioned [here](http://stackoverflow.com/a/2748740/2625036) : `The one main gotcha about moving a bunch of functions into a package is that functions and classes do not automatically import the package they live in.` – Ali Dec 20 '16 at 22:23
  • @Vector The same thing is mentioned in [MathWorks documentation](https://www.mathworks.com/help/matlab/matlab_oop/scoping-classes-with-packages.html): **[Referencing Package Members Within Packages]** All references to packages, functions, and classes in the package must use the package name prefix, unless you import the package. – Ali Dec 20 '16 at 22:34
  • @Ali have you tried it? Private functions within a package **are** reachable from the package (and from each other) without package qualifiers or import statements. Package-level functions do require qualification or import, but private functions that are within the package and called from the package do not. – Vector Dec 22 '16 at 22:34
  • 1
    @Vector Just to be sure. I tested and actually only the "private" folder name seems to work. If I use another name for the private folder, the function inside it is not found (relaunched matlab to make sure). Symbolic links are a nice work around if you want to keep a git submodule containing functions intact for example. – Matthieu Pizenberg Apr 18 '17 at 03:45