Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example does not run as advised: probably need to get rid of weak typing in ismethod #1

Closed
beddalumia opened this issue Sep 6, 2022 · 4 comments

Comments

@beddalumia
Copy link
Contributor

>> foo(true,2)
Index exceeds the number of array elements (1).

Error in dispatch>isa_ (line 73)
        varisa(i) = isa2(var{i},type{i});

Error in dispatch>ismethod (line 42)
        out = isa_(var, numOrType);

Error in dispatch (line 24)
        if ismethod(var, methodTable{i,2})

Error in foo (line 3)
    out = dispatch(varargin,...

Though it works if I remove the dispatch to the "Person" class in the methodTable. I believe this happens because of the effective weak-typed use of numOrType in ismethod.

On a side note I think you probably could enable multiple outputs by just making use of varargout and collecting from the specialized implementation everything as [varargout{1:nargout}]. This was the initial patch I was targeting, but the fundamental design issue (in my view) with collision between dispatch on number of (numeric-only) and on non-numeric types has made me desist. Nevertheless I find the idea of building an "julia-like" ergonomic interface for multiple dispatch via isa quite interesting, I might return on this when I have time: matlab has all the needed functionality for that, just a painful API. Thanks for the idea :)

beddalumia referenced this issue in beddalumia/matlab-multiple-dispatch Sep 6, 2022
Quick fix to problem supporting custom classes.

• Now the a call to foo with a "Person" argument does not fail.
• Note that "dispatch on number of inputs" API has changed, imho it is
  not necessary to allow passing just a number for that, you could just
  pass ["any"] for one input, ["any","any"] for two and so forth…

TODO: extend the implementation to allow multiple output arguments
@aminya
Copy link
Owner

aminya commented Sep 7, 2022

@bellomia Did #3 fix this issue?

@beddalumia
Copy link
Contributor Author

Yes as far as I'm concerned you can close it :)

@aminya
Copy link
Owner

aminya commented Sep 7, 2022

Great! Thanks for confirming. I have to release a new version on the Matlab repository lists

@aminya aminya closed this as completed Sep 7, 2022
@beddalumia
Copy link
Contributor Author

Do you mean the MathWorks File Exchange? I could not find a corresponding entry therein. Anyway I'm planning on trying a deep refactor to enable the "second" great pillar of Julia's approach to code reuse via multiple dispatch: I'm referring to the famous talk by Karpinski, in particular to the slide

Screen Shot 2022-09-08 at 03 23 11

The current implementation does not allow the user of a library which provides generic functions built through dispatch.m to add his own specialized methods, for that he has no access to the body of such wrappers. Don't get me wrong, it is already great for organizing runtime polymorphism in a self-contained Matlab project (much much better than the usual duck typing), but misses imo a great opportunity at enabling ergonomic code reuse.

So what I thought is to encapsulate dispatch() inside a suitable class so that a library would export an object instead of a generic function, as a wrapper of all the methods. Don't worry, I do not mean going full (nor even half) OOP style.

Something like:

>> % init the interface object, 
>> % there are no methods at first 
>> % (but there would be a one-shot constructor eventually, nevermind for now)
>> f = interface 

f = 

  interface with properties:

    method_list: {}

>> % Add a specialized implementation,
>> % by providing a lambda function 
>> % (or a handle of course) and the type signature...
>> f = f.add_method(@(x,y)(x+y),["numeric","numeric"])   

f = 

  interface with properties:

    method_list: {@(x,y)(x+y)  ["numeric"    "numeric"]}

>> h = f.activate % emit a function handle to allow calling the generic as in your implementation

h =

  function_handle with value:

    @(varargin)dispatch(self,varargin{:})

>> h(3,3) % test... and works!

ans =

     6
>>
>> % NOW, imagine this is what the library exports
>> % to the user, not just h(), but also the object f
>> % what we want now is to allow extending f, and
>> % eventually h(), of course....
>>
>> % So let's add another method
>> f = f.add_method(@sin,"numeric")                      

f = 

  interface with properties:

    method_list: {@(x,y)(x+y)  ["numeric"    "numeric"]  @sin  ["numeric"]}

>> h(pi/2) % If I don't "reload" it is not accessed by the handle (but this could make sense I guess...)
Error using interface/dispatch (line 59)
no method found

Error in interface>@(varargin)dispatch(self,varargin{:}) (line 34)
        handle = @(varargin) dispatch(self,varargin{:});
 
>> h = f.activate % So let's activate again the interface

h =

  function_handle with value:

    @(varargin)dispatch(self,varargin{:})

>> h(pi/2)   

ans =

     1
     
>> % hell yeah, successfully added another method,
>> % /dynamically/, which is the crucial requirement.

As you can see the class has just the role of allowing dynamic method extension (which happens automatically in Julia), and I believe should be sealed, to avoid dangerous mixing with OOP patterns: the user should just take the object from
the library and extend the methods (+reload) through the provided class-bound functionality.

All this is already implemented in my fork (in a seminal, rusty, stage for now), you can take a look and try it here. Please note that for now all the syntax is tentative and most probably I would change something, but the UI idea is already all there I think.

Tell me if you could be interested in such a huge modification (I understand if not, as it would drastically change the API). In that case we should move this discussion to a new issue and I could provide a targeting PR in a few days. Otherwise I would continuing exploring the idea on my own fork.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants