Call function in package from package

54 visualizaciones (últimos 30 días)
Andrea Stevanato
Andrea Stevanato el 9 de Mayo de 2018
Comentada: Rob Ewalds el 25 de Abr. de 2023
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?

Respuesta aceptada

Sayan Saha
Sayan Saha el 11 de Mayo de 2018
According to the documentation
all references to functions in the package must use the package name prefix, unless you import the package. See the example in the following link for importing package functions:
  5 comentarios
Rob Ewalds
Rob Ewalds el 19 de Mayo de 2020
Editada: Rob Ewalds el 22 de Abr. de 2021
Very, very poor design. Counterintuitive, counterproductive, not in line with other programming languages either. This must have been a deliberate design decision at some point.
Completely concur with Ralph (29-02-2020), he even spells it out, what to do..
Change this accordingly please.
Rik
Rik el 21 de Abr. de 2021
Comment posted as flag by @Rob Ewalds:
All responders are unanimous in their response: This is poor design, and introduces significant hassle. Could you please stop ignoring all the feedback on this, and pick this up? We don't need 1000+ new features per release, we need a programming-core that gets the basics right. It's clearly a choice: it's not that you don't have the resources.

Iniciar sesión para comentar.

Más respuestas (5)

Danny Kumpf
Danny Kumpf el 30 de Nov. de 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 comentario
Dimitrii Nikolaev
Dimitrii Nikolaev el 13 de Oct. de 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 :(

Iniciar sesión para comentar.


Burcin Bektas
Burcin Bektas el 13 de Ag. de 2020
Editada: per isakson el 13 de Ag. de 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.

Steven Lord
Steven Lord el 21 de Abr. de 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 comentarios
Steven Lord
Steven Lord el 23 de Abr. de 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
Rob Ewalds el 25 de Abr. de 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.

Iniciar sesión para comentar.


Matt Cooper
Matt Cooper el 7 de Nov. de 2022
Movida: Rik el 8 de Nov. de 2022
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.
  2 comentarios
Rik
Rik el 8 de Nov. de 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
Matt Cooper el 8 de Nov. de 2022
Editada: Matt Cooper el 10 de Nov. de 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.

Iniciar sesión para comentar.


Jean Ibarz
Jean Ibarz el 27 de En. de 2023
I agree with nearly everyone here that current Matlab design to avoid naming collisions is very bad.
Please change it.

Categorías

Más información sobre Argument Definitions en Help Center y File Exchange.

Etiquetas

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by