MATLAB Answers

A class to simulate missing arguments in function calls

5 views (last 30 days)
Daniele Busi
Daniele Busi on 7 Feb 2020
Commented: Daniele Busi on 11 Feb 2020
My goal is to design a class named "Unknown", that simulates missing arguments in function calls.
For example, this script
fun(Unknown)
function fun(a)
a %or any other line of code that includes the "a" variable
end
should produce an error in the same way this script does
fun()
function fun(a)
a %or any other line of code that includes the "a" variable
end
Is there any way of achieving this?
My current, hopeless ideas:
  • Overload some sort of "caller" function in the Unknown class to prevent any MATLAB function to be called on an Unknown object - if that "caller" function exists
  • Apply some strange property to the Unknown class that does what I want

  3 Comments

Steven Lord
Steven Lord on 7 Feb 2020
Why are you trying to do that? How do you plan to use this "Unknown" class if you can create it?
There are other techniques that you could use if you want to "skip" an argument (one such technique is to specify the argument as [] and having the function check if it was called with [] as one of its inputs.)
Daniele Busi
Daniele Busi on 7 Feb 2020
Yes, using [] arguments is a solution, but not a very handy one, in my opinion. For example, if I write
fun(1, 2, [], 4)
function x = fun(a, b, c, d)
x = a + b + c + d;
end
the function returns []. Clearly, I would have to check for each input argument to be ~isempty(), or ~isnan(), which could be another technique. However, in this way, a 1-line function becomes a 5-lines function.
Moreover, anonymous functions, which can only be one-line, would benefit from the usage of such "Unknown" class.

Sign in to comment.

Accepted Answer

Matt J
Matt J on 7 Feb 2020
Edited: Matt J on 7 Feb 2020
Clearly, I would have to check for each input argument to be ~isempty(), or ~isnan(), which could be another technique. However, in this way, a 1-line function becomes a 5-lines function.
No, it would become at most a 2-line function. You would naturally write a separate check_for_unknowns.m mfunction that checks all the arguments for Unknowns and re-use that utility in every function that needs it:
fun(1, 2, Unknown, 4);
function x = fun(a, b, c, d)
check_for_unknowns(a,b,c,d)
x = a + b + c + d;
end
function check_for_unknowns(varargin)
map=cellfun(@(c)~isa(c,'Unknown'),varargin);
assert(all(map),'Unknown variable type entered');
end
Moreover, anonymous functions, which can only be one-line, would benefit from the usage of such "Unknown" class.
You can extend this idea to anonymous functions by writing the following additional utility mfile,
function varargout=myfeval(funHandle,varargin)
check_for_unknowns(varargin{:})
[varargout{1:nargout}]=feval(funHandle,varargin{:});
end
and now you can do 1-line things like,
>> fun=@(a,b,c,d) a+b+c+d;
>> x=myfeval(fun,1,Unknown,3,4);

  1 Comment

Daniele Busi
Daniele Busi on 10 Feb 2020
Matt, can I ask you to put your 2 answers together?
This seems to be quite a good solution, as it also allows to determine which element is Unknown (in the myfeval function).
The main drawback is that one must always use myfeval, which is not very readable.

Sign in to comment.

More Answers (3)

Matt J
Matt J on 7 Feb 2020
Edited: Matt J on 7 Feb 2020
Seems to me that you can effectively accomplish what you want by defining a trivial class with no properties or methods,
classdef Unknown
end
Now, pretty much everything you try to do with the class within a function call will raise an error, e.g.,
>> fun=@(a,b,c,d) a+b+c+d;
>> fun(1,Unknown,3,4)
Undefined operator '+' for input arguments of type 'Unknown'.
Error in @(a,b,c,d)a+b+c+d

  0 Comments

Sign in to comment.


Steven Lord
Steven Lord on 7 Feb 2020
So you want your function to be able to accept 0, 1, 2, 3, or 4 input arguments? In that case you probably want varargin.
function x = addThemUp(varargin)
narginchk(0, 4) % Error if addThemUp is called with < 0 inputs (impossible) or > 4 inputs
x = sum([varargin{:}]);
end
or if you want to do this with +:
function x = addThemUp(varargin)
narginchk(0, 4) % Error if addThemUp is called with < 0 inputs (impossible) or > 4 inputs
x = 0;
for whichone = 1:nargin
x = x + varargin{whichone};
end
end
Let's try this with a couple sets of inputs:
>> addThemUp(1, 2, 4)
ans =
7
>> addThemUp(1, 2)
ans =
3
>> addThemUp()
ans =
0
>> addThemUp(1, 2, 3, 4, 5)
Error using addThemUp (line 2)
Too many input arguments.

  0 Comments

Sign in to comment.


Matt J
Matt J on 10 Feb 2020
Edited: Matt J on 10 Feb 2020
The main drawback is that one must always use myfeval, which is not very readable.
Yet another solution, which might address that, is to create your own class of function handles,
classdef Unknown
end
classdef SmartFun %class for functions with prechecks
properties (Constant)
builtins=smartBuiltins;
end
properties
fHandle
end
methods
function obj=SmartFun(fHandle) %constructor
obj.fHandle=fHandle;
end
function varargout=subsref(obj,S)
switch S.type
case '()'
SmartFun.check_for_unknowns(S.subs{:});
[varargout{1:nargout}]=feval(obj.fHandle,S.subs{:});
otherwise
error 'Undefined indexing operation'
end
end
end
methods (Static)
function check_for_unknowns(varargin)
map=cellfun(@(c)~isa(c,'Unknown'),varargin);
assert(all(map),'Unknown variable type entered');
end
end
end
function safecall=smartBuiltins
m=methods('double');
for i=1:numel(m)
safecall.(m{i})=SmartFun(str2func(m{i}));
end
end
and now you can do things like
>> fun=SmartFun( @(a,b,c,d) a+b+c+d );
>> fun(1,2,3,4)
ans =
10
>> fun(1,2,Unknown,4)
Error using SmartFun.check_for_unknowns (line 32)
Unknown variable type entered
Error in SmartFun/subsref (line 17)
SmartFun.check_for_unknowns(S.subs{:});

  3 Comments

Daniele Busi
Daniele Busi on 10 Feb 2020
This is good for function handles, but not for all functions. Say I want to to use the built-in @atan2 function and pass an Unknown as argument. Calling
atan2(Unknown, 0.5)
would raise some generic error like
Undefined function 'atan2' for input arguments of type 'Unknown'.
This does not allow to understand which argument is causing the error, which is a problem especially in much more complex function calls.
This can be avoided by using
fun = SmartFun(@atan2);
fun(Unknown, 0.5);
or
myfeval(@atan2, Unknown, 0.5)
which is fine and clever, just not the ideal best, because it changes (one way or another) the function call syntax. In the first case I need to add a line to instantiate a SmartFun, in the second case I must modify the function call syntax.
This is the reason for the original question. If the Unknown class was able to overload some strange lookForMethod method, which determines the method to call, the same way it can overload subsref, which determines how to index the object, then no myfeval or SmartFun would be needed, and object-specific information could be specified in the error. The ordinary syntax would cause any usage of an Unknown object to call the lookForMethod method, which would have access to the object's properties.
However, I don't think such thing is currently possible, and that's why I have accepted your answer. It is the one that is closest to the ideal.
Matt J
Matt J on 10 Feb 2020
Unfortunately, I think you are right. Matlab does not currently provide a mechanism that lets an object implicitly call a method when it is passed to a function as an argument. The closest thing to that, I think would be subsindex, but that is for indexing expressions, not function call expressions. Therefore, I don't think there is a way to you have what you want with the exact syntax you want.
However, some of the issues you cite above can be mitigated by adding a Constant property to SmartFun, as in my recent edit. This gives a way to pre-instantiate all the methods of class double as SmartFun objects and use them with a more readable, Python-like syntax:
>> safecall=SmartFun.builtins;
>> safecall.atan2(1,1)
ans =
0.7854
>> safecall.sind(90)
ans =
1
>> safecall.sqrt(Unknown)
Error using SmartFun.check_for_unknowns (line 35)
Unknown variable type entered
Error in SmartFun/subsref (line 20)
SmartFun.check_for_unknowns(S.subs{:});

Sign in to comment.

Community Treasure Hunt

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

Start Hunting!