git.fiddlerwoaroof.com
src/main.js
94944213
 function zipWith(fn, ...args) {
     const minLen = Math.min(...args.map(x => x.length));
     const res = [];
 
     for (let x = 0; x < minLen; x++) {
         res.push(fn(...args.map(a => a[x])));
     }
 
     return res;
 }
 
 const gf = GenericFunction(
     "foobar", ["a", "b"]
 ).before(
     [Object, Array], function (a,b) {console.log('in before', this.next_method_p);} 
 ).primary(
     [Object, Array], function (a,b) {
         console.info('next_result: ', this.call_next_method(), this.next_method_p);
         return [a,...b];
     } 
 ).primary(
     [Object, Object], function (thing, single) {
         console.log("hello from previous method", this.next_method_p);
         return [thing, single];
     }
 ).after(
     [Number, Array], function (a,b) {console.log(`in after for ${a}`, this.next_method_p);} 
 ).fn;
 
 function groupGFMessages(gf) {
     return gf.method([around_qualifier], [Object,Object], function(a,b) {
         console.groupCollapsed(gf.name);
         try {
             return this.call_next_method();
         } finally {
             console.groupEnd();
         }
     })
 }
 
 groupGFMessages(gf.gf);
 
 console.log(gf(2,["asdf"]));
 
 const gf2 = GenericFunction(
     "another", ["a"]
 ).primary(
     [Object], function (a) { return {value: a}; }
 ).method(
     [around_qualifier], [Number], function(thing) {
         console.log('before next method in number around');
         const val = this.call_next_method();
         console.log('after next method in number around', val);
         return {was_num: true, ...val};
     }
 ).method(
     [around_qualifier], [Object], function(thing) {
         console.log('before next method in generic around');
         const val = this.call_next_method();
         console.log('after next method in generic around', val);
         return {was_obj: true, ...val};
     }
 );
 
 function MyStore() {
     this.name = 'foo';
     this.address = '1234 asdfadfd'
 }
 
 class NameField extends HTMLElement {constructor() {
     super()
     const style = document.createElement('style');
     this.appendChild(style);
 }}
 customElements.define('the-name', NameField);
 
 class AddressField extends HTMLElement {
     constructor() { super() }
 }
 customElements.define('the-address', AddressField);
 
 const renderFn = defgeneric(
     "dorender", ["component", "el"]
 ).primary(
     [MyStore, NameField], function (comp, heading) {
         console.log('heading el', this.next_method_p);
         heading.textContent = comp.name;
     }
 ).primary(
     [MyStore, AddressField], function (comp, el) {
         console.log('address el', this.next_method_p);
         el.textContent = comp.address;
     }
 ).primary(
     [MyStore, HTMLElement], function (comp, el) {
         console.log('HtmlElement el ', el, this.next_method_p);
         renderFn.fn(comp, el.querySelector('the-name'));
         renderFn.fn(comp, el.querySelector('the-address'));
     }
 ).before(
     [Object, HTMLElement], function (_, el) {
         console.log('has next? ', this.next_method_p)
     }
 );
 
 renderFn.fn(new MyStore(), document.querySelector('section'));