Hi Jake, Thank you for the feedback! You raise some very important concerns and tradeoffs in the design of the API. My short answer is that most of the aspects you raised comes out of the goal of the library being in creating a dynamic-aspect-oriented framework and arose from the API evolved to solve real problem I encountered when developing and using the API, but I'll go into more detail, provide use cases, and you can judge whether there are better solutions to the problems!
Comments (KM>) inline.... On Oct 12, 5:19 pm, Jake Verbaten <rayn...@gmail.com> wrote: > I have no (big) problems with reading coffeescript, i didnt look at your > compiled coffeescript (being forced to read that would be murderous rage). > > Let's take a look at your example, I've replaced your example with my own > mixin method (pd.mixin <https://github.com/Raynos/pd#pd.mixin>) > > // use pd to make example simple :https://github.com/Raynos/pd > pd.extendNatives(); > > var SuperStar = { > constructor: function _construct() { > this.fans = []; > }, > addFan: function _addFan(fan) { > this.fans.push(fan); > return this; > }, > getFans: function _getFans() { > return this.fans; > } > > }; > > var RockStar = {}; > pd.mixin(RockStar, SuperStar); > > // yes Object.prototype.new -https://github.com/Raynos/pd#pd.extendNatives > // is awesome > var rockstar1 = RockStar.new(); > > var Fan = {}; > var fan1 = Fan.new(), fan2 = Fan.new(); > rockstar1.addFan(fan1).addFan(fan2); > console.log(rockstar1.getFans()); > > Live Example <http://jsfiddle.net/nJbXW/1/> > KM> I've looked at your library and examples. Unfortunately, I actually updated the Rockstar example in the README (https:// github.com/kmalakoff/mixin) after posting it here to demonstrate the dynamic nature of the proposed system (basically dynamically adding/ removing mixins to instances on the fly). Also, the example was design to demonstrate the API in a concise way, not to provide an advanced use case to justify the purpose of the library (classic disconnect between real-world problems and examples). I believe the problem you are trying to address is improving the OO nature of Javascript, whereas, the library I am proposing is trying to address a slightly different problem space that could be described as "dynamic aspect- oriented programming". In other words, allowing the library user to respond dynamically to situations and importantly, give them a convention for cleaning up. In some situations, what I propose is unnecessary/overkill and in some situations it fits the bill (but of course, there will always be different ways to solve the same problem) - I included the subscriptions mixin as a more advanced example. I'll try to explain these design considerations in responding to your points below. > Now let's take a moment to go back to the API methods you've shown in your > example. > > *Mixin.registerMixin * > > Why would you want to register a mixin by string name? A mixin should just > be an object. KM> Originally I was using objects. The problem I ran into was that I was using CommonJS that required a path to the object so when I packaged the library and sample mixins I needed to 1) find a way to provide users the flexibility to pick and choose which mixins they use and 2) to not force a specific directory and file structure for them but let them choose how they lay things out. The answer I came up with was "named loose coupling" but if there is a better solution, I'm happy to apply it - just let me know. KM> Use case: one user doesn't use CommonJS and all of their mixins are in the global namespace; another users uses CommonJS and layouts out their mixin files in "vendor", and a different user puts other people's mixins under "vendor/mixin" and their own mixins in a lib folder: "lib/my_mixins" > > *Mixin.initialize* > > The object you "register" has properties like it's name (why should it have > a name, just use the object reference) > It also has an initialize method, why can't that live on the object (my > personal preference is using `.constructor`) KM> Naming conventions aside, there are few reasons for this initialize and destroy being outside the object actually being mixed in: 1) The library is designed to flatten an OO hierarchy using aspects ("aspect" was another name I was considering for the library instead of "mixin") and each aspect can be mixed in and out by the library user when they need. Each time the mixin or mixout happens, the mixin has an opportunity to initialize and cleanup. For example, in much of my code, I use Backbone.Events and jQuery so I bind and unbind events on the fly when mixin in and out. 2) I decoupled the initialize method and put it outside the object was to avoid clobbering between mixins and to not have a fixed new/destroy call order, but something on-the-fly and dynamic. KM> Use cases: in the subscriptions mixin, an Observor can dynamically upgrade an object to have the Subscriber mixin so if their one is destroyed, they automatically cleanup their links to one another. So a Subscriber doesn't need to know beforehand that it will become a subscriber and not all instances of the subscriber class need this functionality and so the cleanup code is only the ones that actually subscribed to something during their lifetime (not all instances of the same "class"). > > *Mixin.in* > > Yes you need that, but why can't this just be `Mixin`. KM> I agree that it is not really an optimal decision for the most used function in the library, but I made this decision mainly because of API symmetry with Mixin.out but also to try to cleanly define namespaces and have everything hang off of one root called Mixin. Of course, the root could also be the mixin function itself. > Also why are you mixin it into instances of classes instead of the classes > themself. That's just rage. A mixin should be done on a class not on every > instance KM> Same answer as to the difference between OO and dynamic aspect- oriented programming and why there is an initialize/destroy not in the actual object. You wouldt need to buy into the use cases for dynamic aspect composition over OO hierachy where each instance may have different compositions, nor the need to cleanup after each mixin. KM> Use case: on-the-fly I adapt Knockout dependent observables handlers based on the actual type of object being observed. To the library user, they are just creating a handler, but behind the scenes the handler is being adapted and cleaned up in a custom way with different dynamic mixins based on what is actually being observed. > > *Mixin.instanceData > > *Why? Seriously, what's wrong with `this.fans = []` why must I store some > data in your framework. KM> This was a decision to avoid property clashes among independent mixin library writers. It quickly became apparent that if the system is generalized and people independently write mixins, there will be an increased probability that their properties will clash. Of course, this doesn't *force* anyone to do anything, it just provides a mechanism for avoiding property clashes if a mixin author believes there is a high risk. Use case: you write a mixin for superstars and I write a mixin for building ventilation management. We both want to use a property called fans = [] Thats verbose and frustrating. KM> There is also a shorter version of the API Mixin.iD() > > *Mixin.out* > > Sorry, what. Why would you ever want to remove a mixin? KM> This function is the way you invoke destroy on a specific mixin (eg. the symmetry between Mixin.in <-> Mixin.out). I could have called it Mixin.destroy, but given that you and mixin and mixout, the naming seemed more self-evident and internally consistent. Also, in my own code, I use one of three life cycle models: "classes" that require no cleanup, new/clone/destroy or new/retain/release so Mixin.out is mainly useful for the last two, but you can cleanup a mixin manually in any case by calling Mixin.out on any instances (for example, those that don't follow one of the final two conventions). KM> Use case: you can mixin (Mixin.in) a specific set of user interface elements to a view, bind their jQuery events, etc; then remove and unbind them (Mixin.out) when the user chooses a sub menu, and then mix them back in when they return (Mixin.in), and finally when your view is destroyed at the end of its lifetime, clean up (Mixin.out). > Now basically, You have a lot of over engineered useless methods on your > library. > > I'm sure there are "some reasons" why they exist but I dont think these > reasons are _any good_. Your going to have to convince people that they > exist > for a good reason. KM> I'm not sure if my use cases have convinced you of the benefits of dynamic aspect-oriented programming. I think your criticisms really "hit many nails on the head" and properly raise the question of is this library over-engineered or at the right level of complexity given the problems it is designed to solve. I've been using it for 3 months on my personal project so I don't have the perspective and am a little enamored by the power and flexibility of the library, but (going back to the nails) - I might be in a situation of "a man with a hammer sees every problem as a nail"...many cases do not require this full functionality, but since it is here, I'm using it generally for classic Javascript mixins in addition to dynamic aspect-oriented programming. > > Normally the place where you "convince" people these methods have a real > purpose is in your (non-existant) documentation. KM> I think you can say "sparse", but "non-existant" is a little strong ;-) Personally, I prefer to look at tests for examples on how to use a library so I really put my focus there and tried to draw the user in the readme to use the tests as reference, but I have also tried to provide various forms of documentation (1) the tests folder has both unit and integration tests (2) there is the README itself (3) there is a docs folder with the project (4) there is my blog: http://braincode.tumblr.com/ and (5) I set up an examples project (https://github.com/kmalakoff/examples-kmalakoff). What do people prefer in terms of documentation anyways? I really appreciate this feedback. Hopefully, I have been able to explain the goals of the library and the use cases it is trying to address. Are you convinced? Either way, I would really like to understand why or why not. Cheers, Kevin -- To view archived discussions from the original JSMentors Mailman list: http://www.mail-archive.com/jsmentors@jsmentors.com/ To search via a non-Google archive, visit here: http://www.mail-archive.com/jsmentors@googlegroups.com/ To unsubscribe from this group, send email to jsmentors+unsubscr...@googlegroups.com