www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.announce - New library: open multi-methods

reply Jean-Louis Leroy <jl leroy.nyc> writes:
Hello,

TL;DR: see here 
https://github.com/jll63/methods.d/blob/master/README.md for an 
explanation of what open multi-methods are, if you are not 
familiar with the idea.You may also want to read my article on 
Code Project 
https://www.codeproject.com/Articles/635264/Open-Multi-Methods-for-Cplusplus11-Part-1

Earlier this year I attended Ali Çehreli's talk at C++ Now. He 
did a good job: I walked out with the desire to learn about D and 
see how it measures up against C++, especially in terms of 
meta-programming and language extensibility. The first 
programming language I learned is Forth and I did some Lisp 
programming, so as you can imagine, my expectations are high.

As an experiment, I decided to try to port parts of my yomm11 
library to D. The experience turned out to be pleasant and I 
ended up writing a full implementation, with some friendly help 
from Ali and others in the Learn forum.

I think that what I have now is good enough to show. The git repo 
is here https://github.com/jll63/methods.d and I will post a 
package to the registry soon.

If you have the inclination, feel free to review and comment. 
This is my very first D project and I certainly have missed some 
idioms and been clumsy at times.

Jean-Louis Leroy
Jul 16
next sibling parent Eugene Wissner <belka caraus.de> writes:
On Sunday, 16 July 2017 at 17:24:17 UTC, Jean-Louis Leroy wrote:
 Hello,

 TL;DR: see here 
 https://github.com/jll63/methods.d/blob/master/README.md for an 
 explanation of what open multi-methods are, if you are not 
 familiar with the idea.You may also want to read my article on 
 Code Project 
 https://www.codeproject.com/Articles/635264/Open-Multi-Methods-for-Cplusplus11-Part-1

 Earlier this year I attended Ali Çehreli's talk at C++ Now. He 
 did a good job: I walked out with the desire to learn about D 
 and see how it measures up against C++, especially in terms of 
 meta-programming and language extensibility. The first 
 programming language I learned is Forth and I did some Lisp 
 programming, so as you can imagine, my expectations are high.

 As an experiment, I decided to try to port parts of my yomm11 
 library to D. The experience turned out to be pleasant and I 
 ended up writing a full implementation, with some friendly help 
 from Ali and others in the Learn forum.

 I think that what I have now is good enough to show. The git 
 repo is here https://github.com/jll63/methods.d and I will post 
 a package to the registry soon.

 If you have the inclination, feel free to review and comment. 
 This is my very first D project and I certainly have missed 
 some idioms and been clumsy at times.

 Jean-Louis Leroy
You may want to use ```d in your code samples in the README to highlight it.
Jul 16
prev sibling next sibling parent reply Jean-Louis Leroy <jl leroy.nyc> writes:
Thinking about it, 'openmethods' would probably be a better 
module/package name than just 'methods'. It emphasizes the #1 
feature, i.e. polymorphism outside of classes.
Jul 17
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Monday, 17 July 2017 at 20:41:05 UTC, Jean-Louis Leroy wrote:
 Thinking about it, 'openmethods' would probably be a better 
 module/package name than just 'methods'. It emphasizes the #1 
 feature, i.e. polymorphism outside of classes.
Googling `multimethods` brought up more programming-related topics than `openmethods`.
Jul 17
next sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Monday, 17 July 2017 at 21:31:20 UTC, jmh530 wrote:
 On Monday, 17 July 2017 at 20:41:05 UTC, Jean-Louis Leroy wrote:
 Thinking about it, 'openmethods' would probably be a better 
 module/package name than just 'methods'. It emphasizes the #1 
 feature, i.e. polymorphism outside of classes.
Googling `multimethods` brought up more programming-related topics than `openmethods`.
Or you could call it omm and then just refer to open multi-methods in the documentation.
Jul 17
parent Jean-Louis Leroy <jl leroy.nyc> writes:
On Monday, 17 July 2017 at 21:32:13 UTC, jmh530 wrote:
 On Monday, 17 July 2017 at 21:31:20 UTC, jmh530 wrote:
 On Monday, 17 July 2017 at 20:41:05 UTC, Jean-Louis Leroy 
 wrote:
 Thinking about it, 'openmethods' would probably be a better 
 module/package name than just 'methods'. It emphasizes the #1 
 feature, i.e. polymorphism outside of classes.
Googling `multimethods` brought up more programming-related topics than `openmethods`.
Or you could call it omm and then just refer to open multi-methods in the documentation.
Yeah that's what the omm in yomm11 means, but I am not too fond of acronyms. In C++ it was the library name (-lyomm11) and also the project name but no the namespace. J-L
Jul 17
prev sibling parent reply Jean-Louis Leroy <jl leroy.nyc> writes:
On Monday, 17 July 2017 at 21:31:20 UTC, jmh530 wrote:
 On Monday, 17 July 2017 at 20:41:05 UTC, Jean-Louis Leroy wrote:
 Thinking about it, 'openmethods' would probably be a better 
 module/package name than just 'methods'. It emphasizes the #1 
 feature, i.e. polymorphism outside of classes.
Googling `multimethods` brought up more programming-related topics than `openmethods`.
Yeah, I know, but I can imagine someone casually browsing the registry, coming across the module and saying "multi-methods? yeah, cool, but I don't remember ever needing such a thing". Indeed "multi" is nice, but IMO "open" is much more important. It is still much more frequent to have only one virtual argument. Take the awful Visitor pattern, for example. It can be neatly replaced with an open method taking only one virtual argument (barring other considerations). 'openmultimethods' is another option but again it emphasizes 'multi'. Anyway, if I go for just 'openmethods', there are enough mentions of 'multi-methods' in the docs. I think I will rename 'methods' to 'openmethods' for the time being, but the discussion remains open. Not renaming the repo yet. Thinking about it, I should add a Visitor replacement example... J-L
Jul 17
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Monday, 17 July 2017 at 22:46:02 UTC, Jean-Louis Leroy wrote:
 I think I will rename 'methods' to 'openmethods' for the time 
 being, but the discussion remains open. Not renaming the repo 
 yet.
On the other hand, when I saw methods, my first thought was R's methods, which I imagine is similar if I'm not mistaken.
Jul 17
parent reply Jean-Louis Leroy <jl leroy.nyc> writes:
On Monday, 17 July 2017 at 22:59:03 UTC, jmh530 wrote:
 On Monday, 17 July 2017 at 22:46:02 UTC, Jean-Louis Leroy wrote:
 I think I will rename 'methods' to 'openmethods' for the time 
 being, but the discussion remains open. Not renaming the repo 
 yet.
On the other hand, when I saw methods, my first thought was R's methods, which I imagine is similar if I'm not mistaken.
I don't know R but after a trip to Wikipedia it looks like it. J-L
Jul 17
parent reply Jay Norwood <jayn prismnet.com> writes:
On Tuesday, 18 July 2017 at 00:47:04 UTC, Jean-Louis Leroy wrote:
 I don't know R but after a trip to Wikipedia it looks like it.

 J-L
R is listed as one of the languages with built-in support in this wiki link. I searched for multiple dispatch because I was familiar with the similar feature in julia, and that's how they refer to it. https://en.wikipedia.org/wiki/Multiple_dispatch An excerpt statement from this wiki page is : " dynamically dispatched based on the run-time (dynamic) type or, in the more general case some other attribute, of more than one of its arguments" Based on the 'some other attribute', I wonder if the library could conceivably be extended to dispatch based on the User Defined Attribute info https://dlang.org/spec/attribute.html ('c') string s; pragma(msg, __traits(getAttributes, s)); // prints tuple('c')
Jul 17
parent Jean-Louis Leroy <jl leroy.nyc> writes:
On Tuesday, 18 July 2017 at 02:22:15 UTC, Jay Norwood wrote:
 An excerpt statement from this wiki page is :
 "  dynamically dispatched based on the run-time (dynamic) type 
 or, in the more general case some other attribute, of more than 
 one of its arguments"


 Based on the 'some other attribute', I wonder if the library 
 could conceivably be extended to dispatch based on the User 
 Defined Attribute info

 https://dlang.org/spec/attribute.html

  ('c') string s;
 pragma(msg, __traits(getAttributes, s)); // prints tuple('c')
For example, CLOS allows you to specialize on a value (google for "eql specialize"). IIRC Clojure allows you to specify your own dispatcher. As for specializing on D attributes, I don't think it's feasible. They are a purely compile-time mechanism. In your example, the type of "s" is "string", not " ('c') string". J-L
Jul 17
prev sibling next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 07/16/2017 10:24 AM, Jean-Louis Leroy wrote:
 TL;DR: see here https://github.com/jll63/methods.d/blob/master/README.md
Woot! :) I'm so happy to see this project complete. Honestly, growing up with languages without this feature (C and C++), I've not even known that I needed this feature but your example (e.g. matrix multiplication) are very convincing. If there are enough differences compared to your C++ articles, perhaps you may consider following this up with a blog post. It would be nice to see some performance results as well like you have on your C++ articles. Ali
Jul 17
next sibling parent reply Jean-Louis Leroy <jl leroy.nyc> writes:
On Tuesday, 18 July 2017 at 04:26:42 UTC, Ali Çehreli wrote:
 On 07/16/2017 10:24 AM, Jean-Louis Leroy wrote:
 TL;DR: see here
https://github.com/jll63/methods.d/blob/master/README.md Woot! :) I'm so happy to see this project complete. Honestly, growing up with languages without this feature (C and C++), I've not even known that I needed this feature but your example (e.g. matrix multiplication) are very convincing.
Thanks :) I added another example that shows how open methods are a superior alternative to Visitor: https://github.com/jll63/methods.d/blob/master/examples/novisitor/source/app.d
 If there are enough differences compared to your C++ articles, 
 perhaps you may consider following this up with a blog post. It 
 would be nice to see some performance results as well like you 
 have on your C++ articles.
Yes I will probably write something. You mean on the D Blog? As for performance, I have a first result: https://github.com/jll63/methods.d/blob/master/benchmarks/sour e/benchmarks.d#L122 but I still have to implement the "first argument optimization". I am working on it. J-L
 Ali
Jul 18
next sibling parent Jean-Louis Leroy <jl leroy.nyc> writes:
On Tuesday, 18 July 2017 at 07:06:10 UTC, Jean-Louis Leroy wrote:
 As for performance, I have a first result: 
 https://github.com/jll63/methods.d/blob/master/benchmarks/sour
e/benchmarks.d#L122 but I still have to implement the "first argument
optimization". I am working on it.
Now this is funny, after implementing that optimization (https://github.com/jll63/methods.d/blob/94ad5a945b3c719bd8f8402bb0aa6fda8e7a6be0/source openmethods.d#L388, https://github.com/jll63/methods.d/blob/94ad5a945b3c719bd8f8402bb0aa6fda8e7a6be0/benchmarks/sourc /benchmarks.d#L139) it runs faster with ldc2 but slower with dmd. I may be testing the limits of dmd's willingness to inline my mess ;-) J-L
Jul 18
prev sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 07/18/2017 12:06 AM, Jean-Louis Leroy wrote:

 Yes I will probably write something. You mean on the D Blog?
Not necessarily but why not. :)
 As for performance, I have a first result:
 
https://github.com/jll63/methods.d/blob/master/benchmarks/source/benchmarks.d#L122
 but I still have to implement the "first argument optimization". I am
 working on it.
I could use some explanation for the results but I can for the blog article. ;) It's not surprising that ldc (and gdc) can be much better than dmd in optimization. By the way, I'm in awe of your D skills in such a short time! I'm sure there are parts of the code that can be cleaned up but it's taking advantage of many powerful features of the language. I still think the usage can be made easier but I'm not sure yet. I hope others will take a look at the code and come up with an easier interface. Perhaps they are all needed but I'm thinking about the need for forward declaration, the need for the underscore prefix, etc. Ali
Jul 18
next sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 18 July 2017 at 16:57:30 UTC, Ali Çehreli wrote:
 Perhaps they are all needed but I'm thinking about the need for 
 forward declaration, the need for the underscore prefix, etc.
He might be able to at least get rid of the forward declaration (not sure on the underscore). The way it works now is that a class that inherits from an interface is not required in any way to implement the methods. Suppose he adds another attribute to an interface such that any class that inherits from it is required to have methods defined for specific functions. So for instance, the Matrix example might look something like trait interface Matrix { property int rows() const; property int cols() const; property double at(int i, int j) const; trait void print(); } I'm not sure this would work because anything that derives from Matrix must implement print. However, if it is possible to use the attribute to allow the derived classes to ignore print, then it might work. Alternately, if there is a way to process the interface and tell the compiler to somehow ignore the trait member functions. I don't know if it'll work, but it's an idea. Anyway, the mixin(registerMethods); could then be adjusted so that void print(virtual!Matrix m); is mixed in automatically because we now know how to construct it.
Jul 18
next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 07/18/2017 11:03 AM, jmh530 wrote:

 the mixin(registerMethods); could then be adjusted so that void
 print(virtual!Matrix m); is mixed in automatically because we now know
 how to construct it.
That reminds me: Would the following be possible and better? // From void main() { updateMethods(); // ... } // To mixin(constructMethods()); void main() { // ... } constructMethods() could return the following string: string constructMethods() { return q{ shared static this() { updateMethods(); } }; } If I'm not missing something, this is better because nothing needs to be added to main and the methods are available before main starts executing (module initialization order issues still apply.). Ali
Jul 18
next sibling parent Jean-Louis Leroy <jl leroy.nyc> writes:
On Tuesday, 18 July 2017 at 18:21:21 UTC, Ali Çehreli wrote:
 That reminds me: Would the following be possible and better?

 // From
 void main()
 {
   updateMethods();
   // ...
 }

 // To
 mixin(constructMethods());
 void main()
 {
   // ...
 }

 constructMethods() could return the following string:

 string constructMethods() {
   return q{
     shared static this() { updateMethods(); }
   };
 }

 If I'm not missing something, this is better because nothing 
 needs to be added to main and the methods are available before 
 main starts executing (module initialization order issues still 
 apply.).
Ah, I would love to get rid of that call in main(), but think beyond a one module program. The matrix example (https://github.com/jll63/methods.d/tree/master/examples/matrix/source) consists in three separate modules, plus an app, all defining specializations. They need the mixin, but if we put updateMehods() in there, it will be called many times. And it is a costly operation. Guarding the call with a flag will not work, because more methods may be registered afterwards. Unless you can think of a way the last mixin can detect it's the last? Incidentally, in yomm11 that function (it's called initialize()) has to be called before any method is called, after any shared object/DLL is loaded and after a DLL is unloaded. I still have to write the code to de-register the methods and the specializations in that case by the way... J-L J-L
Jul 18
prev sibling parent reply Jean-Louis Leroy <jl leroy.nyc> writes:
On Tuesday, 18 July 2017 at 18:21:21 UTC, Ali Çehreli wrote:
 On 07/18/2017 11:03 AM, jmh530 wrote:

 the mixin(registerMethods); could then be adjusted so that
void
 print(virtual!Matrix m); is mixed in automatically because we
now know
 how to construct it.
That reminds me: Would the following be possible and better? // From void main() { updateMethods(); // ... } // To mixin(constructMethods()); void main() { // ... } constructMethods() could return the following string: string constructMethods() { return q{ shared static this() { updateMethods(); } }; }
OK, I think I may have found solutions to both problems. The question is, is it too hacky? 1/ method registration Replace this: import openmethods; mixin(registerMethods); ...with: mixin(import(openmethoddecls)); ...that does the two above. Problem is, it needs -Jpath on the command line to work. Unless there is a workaround? 2/ updateMethods During static construction, I could set the dispatch tables to make all the methods call a function that does updateMethods() then re-dispatches. The cost of the first method call would be huge, but if it matters the user can still call updateMethods explicitly. Thoughts?
Jul 19
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Wednesday, 19 July 2017 at 12:29:36 UTC, Jean-Louis Leroy 
wrote:
 ...that does the two above. Problem is, it needs -Jpath on the 
 command line to work. Unless there is a workaround?
I prefer the original.
Jul 19
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Wednesday, 19 July 2017 at 13:35:40 UTC, jmh530 wrote:
 On Wednesday, 19 July 2017 at 12:29:36 UTC, Jean-Louis Leroy 
 wrote:
 ...that does the two above. Problem is, it needs -Jpath on the 
 command line to work. Unless there is a workaround?
I prefer the original.
What if you do: shared static this(){ mixin(registerMethods); }
Jul 19
parent reply Jean-Louis Leroy <jl leroy.nyc> writes:
On Wednesday, 19 July 2017 at 13:36:55 UTC, jmh530 wrote:
 On Wednesday, 19 July 2017 at 13:35:40 UTC, jmh530 wrote:
 On Wednesday, 19 July 2017 at 12:29:36 UTC, Jean-Louis Leroy 
 wrote:
 ...that does the two above. Problem is, it needs -Jpath on 
 the command line to work. Unless there is a workaround?
I prefer the original.
What if you do: shared static this(){ mixin(registerMethods); }
You mean in openmethods.d?
Jul 19
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Wednesday, 19 July 2017 at 13:46:24 UTC, Jean-Louis Leroy 
wrote:
 What if you do:

 shared static this(){

   mixin(registerMethods);

 }
You mean in openmethods.d?
Yes. I haven't tried something like that, but it seems like a use case for either static this or shared static this. https://dlang.org/spec/class.html#StaticConstructor https://dlang.org/spec/class.html#SharedStaticConstructor
Jul 19
next sibling parent reply Jean-Louis Leroy <jl leroy.nyc> writes:
On Wednesday, 19 July 2017 at 15:33:28 UTC, jmh530 wrote:
 On Wednesday, 19 July 2017 at 13:46:24 UTC, Jean-Louis Leroy 
 wrote:
 What if you do:

 shared static this(){

   mixin(registerMethods);

 }
You mean in openmethods.d?
Yes. I haven't tried something like that, but it seems like a use case for either static this or shared static this. https://dlang.org/spec/class.html#StaticConstructor https://dlang.org/spec/class.html#SharedStaticConstructor
Among other things, the mixin introduces two functions in the module's scope: the function the user actually calls (the "dispatcher"). E.g. it creates a times(double, Matrix) when it sees a times(double, virtual!Matrix). It also declares a "discriminator" function which is used to locate which method the specializations (the method funcs) relates to (it has to deal with overloads - there are two "times" methods). This has to be done for every module that contains method declarations (virtual!) or implementations ( method). That's why it has to be a string mixin (at least until we have static foreach) and be called in the matrix etc modules, not in module openmethods.
Jul 19
parent jmh530 <john.michael.hall gmail.com> writes:
On Wednesday, 19 July 2017 at 15:56:06 UTC, Jean-Louis Leroy 
wrote:
 Among other things, the mixin introduces two functions in the 
 module's scope: the function the user actually calls (the 
 "dispatcher"). E.g. it creates a times(double, Matrix) when it 
 sees a times(double, virtual!Matrix). It also declares a 
 "discriminator" function which is used to locate which method 
 the specializations (the  method funcs) relates to (it has to 
 deal with overloads - there are two "times" methods). This has 
 to be done for every module that contains method declarations 
 (virtual!) or implementations ( method). That's why it has to 
 be a string mixin (at least until we have static foreach) and 
 be called in the matrix etc modules, not in module openmethods.
I see what you mean. It only works per thread, not per module. What you really need is some kind of module constructor.
Jul 19
prev sibling parent jmh530 <john.michael.hall gmail.com> writes:
On Wednesday, 19 July 2017 at 15:33:28 UTC, jmh530 wrote:
 Yes. I haven't tried something like that, but it seems like a 
 use case for either static this or shared static this.

 https://dlang.org/spec/class.html#StaticConstructor
 https://dlang.org/spec/class.html#SharedStaticConstructor
Based on some of your README.md text, you may need to do static this rather than shared static this for what I suggested. I don't really know, but worth investigating. I liked Ali's suggestion for mixin updateMethods. Again, not sure if it should be static this or shared static this, but if you have static this for registerMethods, then a shared static this for updateMethods may occur before that. I'm not sure how important the order is.
Jul 19
prev sibling parent reply Jean-Louis Leroy <jl leroy.nyc> writes:
On Tuesday, 18 July 2017 at 18:03:30 UTC, jmh530 wrote:
 On Tuesday, 18 July 2017 at 16:57:30 UTC, Ali Çehreli wrote:
 Perhaps they are all needed but I'm thinking about the need 
 for forward declaration, the need for the underscore prefix, 
 etc.
He might be able to at least get rid of the forward declaration (not sure on the underscore). The way it works now is that a class that inherits from an interface is not required in any way to implement the methods. Suppose he adds another attribute to an interface such that any class that inherits from it is required to have methods defined for specific functions. So for instance, the Matrix example might look something like trait interface Matrix { property int rows() const; property int cols() const; property double at(int i, int j) const; trait void print(); } I'm not sure this would work because anything that derives from Matrix must implement print. However, if it is possible to use the attribute to allow the derived classes to ignore print, then it might work. Alternately, if there is a way to process the interface and tell the compiler to somehow ignore the trait member functions. I don't know if it'll work, but it's an idea. Anyway, the mixin(registerMethods); could then be adjusted so that void print(virtual!Matrix m); is mixed in automatically because we now know how to construct it.
There are at least problems with this. Firstly it is intrusive - something I strive to avoid (although I could be 100% orthogonal only because I hijack a deprecated pointer in ClassInfo). Also, some methods may want to treat Matrix as a virtual argument, and some not. Look at https://github.com/jll63/methods.d/blob/master/examples/matrix/source/matrix.d and https://github.com/jll63/methods.d/blob/master/examples/matrix/ ource/densematrix.d They know nothing about printing. They don't want to. The matrix modules do math, the app does printing. J-L
Jul 18
next sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 07/18/2017 12:22 PM, Jean-Louis Leroy wrote:

 Look at
 
https://github.com/jll63/methods.d/blob/master/examples/matrix/source/matrix.d
 and
 
https://github.com/jll63/methods.d/blob/master/examples/matrix/source/densematrix.d
 They know nothing about printing. They don't want to. The matrix modules
 do math, the app does printing.
Related, our friend Luís Marques was the speaker in January 2016 here at the DLang Silicon Valley meetup. "A defense of so-called anemic domain models": https://www.meetup.com/D-Lang-Silicon-Valley/events/228027468/ I'm totally sold on the idea. Ali
Jul 18
prev sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 18 July 2017 at 19:22:38 UTC, Jean-Louis Leroy wrote:
 Look at 
 https://github.com/jll63/methods.d/blob/master/examples/matrix/source/matrix.d
and https://github.com/jll63/methods.d/blob/master/examples/matrix/
ource/densematrix.d They know nothing about printing. They don't want to. The
matrix modules do math, the app does printing.

 J-L
I may not have been clear enough. My ideal solution wouldn't make any changes to that densematrix.d file, just the interface. So I don't have any issue with the matrix modules doing the math and the app doing the printing. For instance, consider the traits in Rust https://doc.rust-lang.org/book/first-edition/traits.html My idea is like making the interfaces in D similar to the traits in Rust (or at least having the option to do something similar with them). Your method void _print(Matrix m) would be similar to impl print for Matrix in Rust. Nevertheless, I get that it may be a difficult thing to implement.
Jul 18
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 18 July 2017 at 21:16:11 UTC, jmh530 wrote:
 I may not have been clear enough. My ideal solution wouldn't 
 make any changes to that densematrix.d file, just the 
 interface. So I don't have any issue with the matrix modules 
 doing the math and the app doing the printing.
Well, I suppose the matrix interface would be saying that it can print, so maybe not as split up as you would like. While you could define a separate interface for printing, that would require a change to densematrix.
Jul 18
parent Jean-Louis Leroy <jl leroy.nyc> writes:
On Tuesday, 18 July 2017 at 21:20:04 UTC, jmh530 wrote:
 On Tuesday, 18 July 2017 at 21:16:11 UTC, jmh530 wrote:
 I may not have been clear enough. My ideal solution wouldn't 
 make any changes to that densematrix.d file, just the 
 interface. So I don't have any issue with the matrix modules 
 doing the math and the app doing the printing.
Well, I suppose the matrix interface would be saying that it can print, so maybe not as split up as you would like. While you could define a separate interface for printing, that would require a change to densematrix.
Exactly. Orthogonality is essential for good composition, that is the reason why OOP - well, the OOP that follows the Simula/Smalltalk tradition - failed so badly. CLOS got it right 40 years ago; Simula, Smalltalk, C++, Java, etc they all got it wrong.
Jul 18
prev sibling parent Jean-Louis Leroy <jl leroy.nyc> writes:
On Tuesday, 18 July 2017 at 16:57:30 UTC, Ali Çehreli wrote:
 As for performance, I have a first result:
 
https://github.com/jll63/methods.d/blob/master/benchmarks/source/benchmarks.d#L122
 but I still have to implement the "first argument
optimization". I am
 working on it.
I could use some explanation for the results but I can for the blog article. ;)
I pit a method-based call against its equivalent using virtual functions. First calling a virtual function via a base class is pitted against a method with one virtual parameter. Then the same but calling via an interface. Lastly, I compare double dispatch with a method with two virtual arguments. I use std.datetime.comparingBenchmark, which reports the result as time(base)/time(target). So open methods are a bit slower than ordinary virtual function calls but not that much. In the meantime I have applied a second optimization for unary methods and this brings them within 33% of an ordinary, compiler implemented vfunc call. Which is OK because the situation is highly artificial. If the function does anything, the difference will be imperceptible. I am more annoyed by double dispatch beating binary methods. I will have to look at the assembler, but it may be that the index pointer is too far from the object. To begin the real work, I need to fetch that pointer form an object. Currently it is stored in ClassInfo.deallocator, so I have to 1/ fetch the vptr 2/ fetch the ClassInfo* 3/ fetch 'deallocator'. What happens next depends on the arity. Any chance of Walter giving me a pointer in the vtable? Aside the ClassInfo*? Or at least a pointer in ClassInfo, or reassign the deallocator when it is eventually retired?
 It's not surprising that ldc (and gdc) can be much better than 
 dmd in optimization.
I would like to try gdc but it conflicts with ldc2 - you know, the "alias __va_list = __va_list_tag;" issue. I found suggestions via google but nothing worked for me so far.
 By the way, I'm in awe of your D skills in such a short time!
Thanks :) I found out that D was much more natural, "predictable" than C++. The most cryptic error messages happened when I forgot the "!", IIRC.
 I'm sure there are parts of the code that can be cleaned up but 
 it's taking advantage of many powerful features of the 
 language. I still think the usage can be made easier but I'm 
 not sure yet. I hope others will take a look at the code and 
 come up with an easier interface. Perhaps they are all needed 
 but I'm thinking about the need for forward declaration, the 
 need for the underscore prefix, etc.
(in reverse order) Regarding the prefix, it is needed to prevent overload resolution from trumping dynamic dispatch - see here: https://github.com/jll63/methods.d/blob/master/examples/whytheund rscore/source/app.d Allowing the same name would necessitate compiler support. As for the the forward declaration - I don't think it is possible to dispense with it. All open methods systems I know of have it (defgeneric in CLOS, defmulti in Clojure, Stroustrup's proposal...). Consider: class A { } class B : A { } class X : B { } class Y : B { } method void _foo(virtual!X x) { ... } method void _foo(virtual!Y x) { ... } What is the base method? foo(B)? foo(A)? It may well be the latter. Also don't forget that the complete specialization set is known, at the earliest, at link time. If you (arbitrarily) pick foo(B), another module may introduce a B or an A specialization. As for suggestions and advise, they are very welcome :) already got a couple of PRs. Here are the remaining questions on my mind: - the module/package name: I am pretty much set on openmethods though... - throw an exception if a method is not define for the argument set, or ambiguous: having big doubts about this. We want the possibility of nothrow methods, don't we? So I will probably call a delegate via a pointer (a la C++) which, by default, will abort(). - the method prefix: hesitating between just _ or maybe m_ ??? - replace version(explain) with debug levels?
Jul 18
prev sibling parent Jean-Louis Leroy <jl leroy.nyc> writes:
On Tuesday, 18 July 2017 at 04:26:42 UTC, Ali Çehreli wrote:
 It would be nice to see some performance results as well like 
 you have on your C++ articles.
Lib is in the dub registry now. Do you have a working gdc compiler? If yes, could you run the benchmark and post the results? Please make sure to build in release mode.
Jul 19
prev sibling next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 07/16/2017 10:24 AM, Jean-Louis Leroy wrote:
 Hello,

 TL;DR: see here https://github.com/jll63/methods.d/blob/master/README.md
Added D to the Wikipedia entry, which can be expanded. :) https://en.wikipedia.org/wiki/Multiple_dispatch Ali
Jul 18
parent Jean-Louis Leroy <jl leroy.nyc> writes:
On Tuesday, 18 July 2017 at 22:41:13 UTC, Ali Çehreli wrote:
 On 07/16/2017 10:24 AM, Jean-Louis Leroy wrote:
 Hello,

 TL;DR: see here 
 https://github.com/jll63/methods.d/blob/master/README.md
Added D to the Wikipedia entry, which can be expanded. :) https://en.wikipedia.org/wiki/Multiple_dispatch Ali
Haha that settles it then, openmethods it is! I'll rename the repo and upload to the registry momentarily.
Jul 18
prev sibling next sibling parent reply James Dean <JamesDean MTV.com> writes:
On Sunday, 16 July 2017 at 17:24:17 UTC, Jean-Louis Leroy wrote:
 Hello,

 TL;DR: see here 
 https://github.com/jll63/methods.d/blob/master/README.md for an 
 explanation of what open multi-methods are, if you are not 
 familiar with the idea.You may also want to read my article on 
 Code Project 
 https://www.codeproject.com/Articles/635264/Open-Multi-Methods-for-Cplusplus11-Part-1

 Earlier this year I attended Ali Çehreli's talk at C++ Now. He 
 did a good job: I walked out with the desire to learn about D 
 and see how it measures up against C++, especially in terms of 
 meta-programming and language extensibility. The first 
 programming language I learned is Forth and I did some Lisp 
 programming, so as you can imagine, my expectations are high.

 As an experiment, I decided to try to port parts of my yomm11 
 library to D. The experience turned out to be pleasant and I 
 ended up writing a full implementation, with some friendly help 
 from Ali and others in the Learn forum.

 I think that what I have now is good enough to show. The git 
 repo is here https://github.com/jll63/methods.d and I will post 
 a package to the registry soon.

 If you have the inclination, feel free to review and comment. 
 This is my very first D project and I certainly have missed 
 some idioms and been clumsy at times.

 Jean-Louis Leroy
Interesting. One problem I think the above approach has is adding methods after compilation. Say, a plugin adds a new derived matrix type SparseMatrix and wants to customize the addition of them. This is impossible under the current model, is it not? Would it not be possible create a sort of "externmultimethod" that mimics extern'ing a method? Basically, on the "server/host" side there is a method that can be used to add new multimethods at runtime. It takes meta data and extends the virtual table to handle dispatching it along with the other functions. The "client/plugin" side has the multimethod it wants to add to the dispatch and it does this by giving it all the needed information to do so and using the new externmultimethod method to do it.
Jul 18
parent Jean-Louis Leroy <jl leroy.nyc> writes:
On Wednesday, 19 July 2017 at 06:27:40 UTC, James Dean wrote:
 Interesting. One problem I think the above approach has is 
 adding methods after compilation. Say, a plugin adds a new 
 derived matrix type SparseMatrix and wants to customize the 
 addition of them. This is impossible under the current model, 
 is it not?
Why? I haven't tried it yet (putting together an example is one of the TODOs before v1.0.0) but I fully expect it to work. The dispatch tables are created at compile time. Just call updateMethods after loading or unloading the DLL and it should work. It does in the C++ version.
Jul 19
prev sibling parent Jean-Louis Leroy <jl leroy.nyc> writes:
openmethods is now available in the dub registry: 
https://code.dlang.org/packages/openmethods
Jul 19