Call function in package from package
Mostra commenti meno recenti
How I can use use function present in package from the package? If i have package with:
+mypkg
fun1.m
fun2.m
I would that in fun1.m i can call fun2.m. I only could call fun2.m with mypkg.fun2? There are some alternative?
Risposta accettata
Più risposte (5)
Danny Kumpf
il 30 Nov 2020
I also agree that this is poor design, and here is another reason why: Let's say I have a package +pkg1 that is a dependency of a couple other packages I'm working on (+pkg2 and +pkg3). If I put +pkg1 into +pkg2, then not only do I have to call pkg1's internal functions with pkg1.some_fn(), I also have to preface them with pkg2: pkg2.pkg1.some_fn(). This sucks if I want to use pkg1 as a dependency of pkg3 as well, becuase now I need two versions of pkg1: one that calls its internal functions like pkg2.pkg1.some_fn(), and one that calls them with pkg3.pkg1.some_fn().
Here's my hacky workaround.
Paste the following line to be the first line of every function within +pkg1 that calls another pkg1 function:
eval(sprintf('import %s.*', strjoin(regexp(mfilename('fullpath'), '(?<=+)\w*', 'match'), '.')));
This line:
- Gets the full file path, eg C:\repos\+pkg2\+pkg1\some_fn.m
- Uses regexp to find all packages on the path, ie `+<pkg_name>` sub-strings
- Joins the package names together with '.', eg: `pkg2.pkg1`
- Uses `eval()` to call matlab's `import` statement: `import pkg2.pkg1.*`
After executing, you can call any functions that exist in pkg1 directly, without including the package names. This means that, from within a pkg1 function, you don't need to know whether pkg1 is a package, or whether it is a sub-package of any other package(s). This works even if pkg1 itself is NOT a package, but instead is just added to the matlab path (because `import .*` does nothing). This means a user could use the same repo either as a package or on the matlab path, without changing the code.
But, some negatives are that this line:
- Uses `eval()` which I think is frowned upon
- Forces you to do a regexp every time you call a package function, which could be slow
- May cause namespace collisions if the imported package shares a function name with something else?
1 Commento
Dimitrii Nikolaev
il 13 Ott 2021
Qiute the usefulliest comment over here.
Situations, when you need to subclass some class, nested in a package like:
classdef some_subclass < pkg2.pkg1.some_class
or check argument of a function using arguments syntax for validation like:
arguments %<--BTW, funny, that this keyword is not highlighted now :/
someargument (1,1) pkg2.pkg1.some_class
end
will remain a unsolveable problem :(
Burcin Bektas
il 13 Ago 2020
Modificato: per isakson
il 13 Ago 2020
Agree with everyone above that this is poor design. I did find a hack that seems to work:
someObject=packageNameThatIShouldntHaveToWrite.SomeClass
can be written as
eval(['someObject = ', metaclass(obj).ContainingPackage.Name, '.SomeClass;', ]);
if calling from a class. "obj" above is the object variable SomeClass constructor returns.
Jean Ibarz
il 27 Gen 2023
2 voti
I agree with nearly everyone here that current Matlab design to avoid naming collisions is very bad.
Please change it.
Steven Lord
il 21 Apr 2021
Everything I'm writing here is my own opinion about what would happen if we made the change requested in this discussion thread.
Let's assume that package functions could call other functions in the same package without using the package name. What impact would that have? In particular how would you call a function from outside any package if there's a function by the same name in the package? Could I call the built-in bar function from within mypackage.foo if I had a mypackage.bar function?
In order to insulate yourself from files being added to the package breaking your code, each and every "global" function that you call would need to be called in that way. If foo is a function in a package:
function y = foo(x)
y = sin(x).^2-2*cos(x).^3;
end
Does that call the built-in sin function? It would be impossible to say without knowing if there's a sin function in the package. The answer to that question could change with no modification to the foo package function! So to safeguard and assure you're calling the built-in sin function let's assume that all the "global" functions were treated as being in a package named globalfuns. Do you want to have to write the following?
function y = foo(x)
y = globalfuns.sin(x).^2-2*globalfuns.cos(x).^3; % Calls the built-in sin and cos functions
end
That's a lot more verbose than the first version of the function. But that's not the whole story. The .^ operator has another name: it is the power function. So if you had a power function in your package would .^ call that function? Again, that would be impossible to say. To be sure it wouldn't you'd need:
function y = foo(x)
y = globalfuns.power(globalfuns.sin(x),2)-2*globalfuns.power(globalfuns.cos(x),3);
end
But the * operator is also called mtimes.
function y = foo(x)
y = globalfuns.power(globalfuns.sin(x),2)-globalfuns.mtimes(2,globalfuns.power(globalfuns.cos(x),3));
end
That's a lot less readable than the first case. But that's what you would need to do to make sure that people can't break your function by adding power, sin, mtimes, or cos functions to your package.
With the current system and the original function:
function y = foo(x)
y = sin(x).^2-2*cos(x).^3;
end
you would have to opt into calling the package sin or cos functions if you wanted to call them. Otherwise you get the built-in functions. People could add functions to or remove functions from the package without affecting the foo function above.
How do you opt in?
function y = foo(x)
y = mypackage.sin(x).^2-2*cos(x).^3; % Use the package sin, built-in cos
end
% or
function y = foo(x)
import mypackage.*
y = sin(x).^2-2*cos(x).^3; % Use mypackage.sin and/or mypackage.cos if they exist, built-ins if not
end
For package functions, the package name is effectively part of the function's name. [Not for purposes of a function like isvarname but it is for purposes of calling the function.] Just as you would have to update the callers of a regular function myfun if you changed then name from myfun to mybar to ensure they keep calling the correct function, if you change the name of mypackage.myfun to mynewpackage.myfun you need to update the callers whether they be in the mypackage package, the mynewpackage function, or outside any package.
In my opinion, changing either the file name or the package name should be a rare occurrence representing a major change in the organization and architecture of your code. It's not something you should do on a whim, and you should allocate time to react to the major reorganization / rearchitecting of the code.
9 Commenti
RAB
il 21 Apr 2021
How is the requested behavior different from the use of private functions as supported by MATLAB?
Functions in the package could be considered private to the package and take precedence - solved.
Overloading issues of functions is not unique to packages but happens based on path already.
If you want to use a function from outside the package use another function name within the package.
Rob Ewalds
il 22 Apr 2021
"fun1 should know it resides within package mypkg and make all subsequent function calls to members of that package when they exist. The affect is to make the contents of mypkg the highest search order in the path for functions within mypkg. Note: this is how it should be implemented, not how it currently exists as of build 2019B."
Danny Kumpf
il 22 Apr 2021
Modificato: Danny Kumpf
il 27 Apr 2021
"In my opinion, changing either the file name or the package name should be a rare occurrence representing a major change in the organization and architecture of your code."
But what about using a package inside of another package? If I put package2 into package1, then every internal function call within package2 has to be prefaced by package1.package2.function(). That's an extremely common use case that's simply impossible to do well with the current implementaiton of packages.
As to your concerns about namespace collisions, I don't understand why this is more of a problem for MATLAB than for any other programming language. In every other language I've used, functions can see files in their own package without using any namespace specifier. If you overwrite some global, then that's fine, it just uses the local version. It's on the user to avoid doing this. It's not that hard for the IDE to warn you if you're overriding a builtin MATLAB function. If I want to use MATLAB's global functions in my packages, all I have to do is not override them.
It looks to me that this is the tradeoff you're making with this design choice:
- We give up the ability to have nested sub-packages,
- We gain the ability to name functions in our packages the same names as MATLAB global functions.
What we're giving up is extremely useful and would be used all the time. What we're gaining is totally useless and is probably bad code style anyway even though it's technically allowed.
Note, you could at least partially change my mind on this if you can point to a good way in MATLAB to do nested packages. Eg, some method (other than just putting directories on my path) that lets me use independently-maintained packages as git submodules for other packages. Right now I don't see any way to do that, except for the hacky solution I give in my answer to this question below. If I want the package functionality of clear namespace prefixes (for package functions called from outside the package, that is), then I must sacrifice the ability to have nested (independently-maintained) packages.
Rob Ewalds
il 25 Mag 2021
We're not convinced really, and the matter continues to be a pressing concern for us.
Ashley Trowell
il 14 Lug 2021
This is also a big issue for me. Mathworks shouldn't actively discourage good coding style in their design choices.
I'm not convinced by the argument that making packages hard to use prevents name collisions. In fact, this is the opposite of the truth, packages PREVENT collisions, that's what a namespace IS. Clear example: I created an object oriented simulation architecture, which among other things, has class names like Platform, Antenna, and Radar. This suite of classes is many thousands of lines of code, and perhaps a hundred m-files I use these a lot, so they sit in my path. Now it turns out, I'm not the only person who like names like Platform, Radar, and Antenna, and before long, I'm experienceing collisions. The sensible thing would be to wrap my project in a package. It wouldn't be too bad for me to have to type radar = myPackage.Radar() in the future. However... now I have thousands of lines of code that need to be checked for references between classes. In some cases, there is a major ergonomics challenge, since a line of code might have called a few disparate functions and been fine before
obj.newValue = fun1(1,2,3) * fun2(1,2,3) + fun3(1,2,3)
But now:
obj.newValue = myPackage.fun1(1,2,3) * myPackage.fun2(1,2,3) + myPackage.fun3(1,2,3)
This is suddenly not so fun. It encourages using a shorter package name than would really be advisable. It also makes simple find/replace refactoring hard to implement.
I don't see why be can be worried that I might have myPackage.plot defined when I can at anytime overwrite the default plot function with an accidental plot = 1.
Furthermore, this leads to several IDE issues which make a bad situation worse. First, there is a distinct lack of refactoring tools. Even if changing a package name is considered a big change, to be honest, I like to make big changes quickly. I've got other things to do than rename import statements.
Secondly, using import statements breaks my ability to use Ctrl+D to trace to a functions definition. Folks trying to understand the code can end up perplexed for a while until they go looking for an import, which they rarely ever do.
Eric Vowinkel
il 23 Apr 2023
@Steven Lord Any response to @Danny Kumpf and @RAB? @Rob Ewalds also requested a response? They all make very valid points that the currently implementation is bad. All it does it make people hesitant to use an otherwise very useful tool that is Packages, since calling a simple function requires you to first prefix it with a potentially very long package name, or with even multiple package names for nested packages. Things get very messy, very quickly when you're trying to build a toolset. Packages should be expected to be scope to their "namespaces", and shouldn't require these prefixes.
Rob Ewalds
il 23 Apr 2023
We perceive the lack of in-depth response as truly dissappointing.
Steven Lord
il 23 Apr 2023
As I very clearly stated at the start of my response, what I wrote was just my opinion. If you want to file an official enhancement request with MathWorks please contact Technical Support directly using the Contact Support link under the Get Support heading at the end of this page.
Rob Ewalds
il 25 Apr 2023
>> If you want to file an official enhancement request with MathWorks
>>please contact Technical Support
We did. Quite some time ago, already. Refer to closed case-number: 04255246
The reply was:
>>I am unable to say whether or not this change will occur in future versions of MATLAB;
>>however, I will advocate that the developers consider this change for future releases on your behalf.
Not much happened, obviously. So we don't really see the benefit of filing yet another issue report.
This is a persistent pain for many developers. MathWorks please act accordingly.
Matt Cooper
il 7 Nov 2022
Spostato: Rik
il 8 Nov 2022
1 voto
This doesn't answer the OP's question or solve all of the issues you raise, but here's what I did to convert an existing project into a package. First, to address the "shorter package name than would be advisable", put the package in a folder with a descriptive name, and the package contents in ONE subfolder, which is an acronym for the descriptive name: DescriptiveProjectName/+dpn. Then simply do an fscanf,strrrep,fprintf workflow to read in each function in +dpn/ and replace all function calls with new function calls that have dpn. appended to them. In other words, calls to Platform, Radar, and Antenna would be replaced by dpn.Platform, dpn.Radar, and dpn.Antenna. Alternatively, you can use the same fscanf,strrrep,fprintf workflow to include a wildcard import statement at the top of each function in your package, instead of appending the package prefix to every function call. This roughly mirrors a typical python namespace with import statements at the top of a calling script that replace descriptive package names with acronyms, and all subsequent function calls use the acronym prefix e.g. import matplotlib.pyplot as plt followed by plt.plot(...).
I wrote a quick script to do this for my package and attached it here. In my case, the project already had an acronym as a prefix on all function filenames e.g. dpn_func1, dpn_func2, ... and so on (which is fairly common). This simplified the find/replace logic, but also meant I had to replace the H1 line and the function name in my inputParser calls, and I used a system command to issue git mv for each file. If your project code doesn't have a common prefix, you can just generate a list of function names in your package folder using dir and do a strcmp. If you have lots of subfolders/subpackages it will get more complicated (which I did), but I found it much more pleasing to collapse all my subfolders/packages into one folder, and it no longer seems necessary or beneficial to have a bunch of subfolders/packages (and greatly simplifies the functionSignatures.json file). But there's no reason you cannot keep the subfolders/packages, you just need to loop through and apply something similar to the script I attached.
5 Commenti
Rik
il 8 Nov 2022
I moved your comment to be an answer, since it provides a solution/workaround for the issue in this thread. This way it can also be more easily be found again later by others. If you think it should have stayed a comment, feel free to comment below and I'll revert the change (or Steven might).
Matt Cooper
il 8 Nov 2022
Modificato: Matt Cooper
il 10 Nov 2022
Sounds good, thank you. I attached a new version of the script with documentation and some safety measures (dryrun flag) to prevent accidental code rewrites.
Bogdan -Ervin
il 27 Lug 2025
Hello!
I know that in your file you have provided a contact channel for questions, but it is not explicit enough for me to understand how to contact you, so I will be asking you here.
Your script works to put the namespace prefix even for classes?
Walter Roberson
il 27 Lug 2025
@Matt Cooper included the contact information
guycooper (a) ucla dot edu
To use it, replace the "(a)" with "@" and replace the "dot" with "." and remove the spaces
The result would look like
somethingsomethingsomething@ucla.edu
Bogdan -Ervin
il 11 Ago 2025
Categorie
Scopri di più su Software Development Tools in Centro assistenza e File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!