staltz / callbag-skip

👜 Callbag operator that skips the first N data points of a source
MIT License
4 stars 2 forks source link

Handmade optimizations? #5

Open PEZO19 opened 2 years ago

PEZO19 commented 2 years ago

@staltz

Hi there! I am not sure how useful this is or how useful it ever can be, but while studying callbags I realized that both the theoretical protocol (using 0,1,2 numbers extensively) and the exact operator implementations (via neat and succinct callbacks) make room together for handmade optimizations. (We used to do such things as homework at the uni, so I know that for many this literally can be fun, I feel that the callbag abstraction truly enables that by its nature.)

I am wondering if that can be useful at scale (more operators) and what kind of performance tests or other things could be created to be really useful - and of course what shall be the target ES version...

I just want to put here an example how I progressed with that (I know some tricks, but I am not a pro in that area), maybe some folks will think it further.

I think we might find quite a lot patterns across operators. A few notes / examples / interesting things:

The right side of the assignment went from 102 chars: t=>e=>(i,n)=>{if(0!==i)return;let r,s=0;e(0,(e,i)=>{0===e?(r=i,n(e,i)):1===e&&s<t?(s++,r(1)):n(e,i)})} to 74 chars: i=>s=>(t,c)=>{!t&&(s(0,(s,f)=>{!s?(t=f,c(s,f)):1==s&&0<i--?t(1):c(s,f)}))} -27% :)

Tests passed, however I realized that omitting the first conditional (originally the return part) won't make tests fail, I tried to keep that there though.

I used this minifier as a starter: https://skalman.github.io/UglifyJS-online/


// this is a close, "reverted" version from the last one at the bottom (`skipA`)
const skip = max => source => (start_then_Talkback, sink) => {
  if (!start_then_Talkback) {
    // let skipped = 0;
    // let talkback;
    source(0, (t, d) => {
      if (t == 1) {
        if (0 < max--) {
          start_then_Talkback(1);
        }
        else sink(t, d);
      }
      else if (t == 0) {
        start_then_Talkback = d;
        sink(t, d); // c(s,f) below
      }
      else {
        sink(t, d);
      }
    });
  };
}

// NOTE COMMENTS ON THE RIGHT >>>>>

const skip1=t=>e=>(i,n)=>{if(0!==i)return;let r,s=0;e(0,(e,i)=>{0===e?(r=i,n(e,i)):1===e&&s<t?(s++,r(1)):n(e,i)})}; // baseline
const skip2=i=>s=>(t,c)=>{if(0===t){let t,e=0;s(0,(s,f)=>{0===s?(t=f,c(s,f)):1===s&&e<i?(e++,t(1)):c(s,f)})}}; // omit `return`
const skip3=i=>s=>(t,c)=>{if(0==t){let t,e=0;s(0,(s,f)=>{0==s?(t=f,c(s,f)):1==s&&e<i?(e++,t(1)):c(s,f)})}};    // double == instead ===
const skip4=i=>s=>(t,c)=>{if(!t){let t,e=0;s(0,(s,f)=>{0==s?(t=f,c(s,f)):1==s&&e<i?(e++,t(1)):c(s,f)})}};      // !t instead 0==t
const skip5=i=>s=>(t,c)=>{if(!t){let t,e=0;s(0,(s,f)=>{!s?(t=f,c(s,f)):1==s&&e<i?(e++,t(1)):c(s,f)})}};        // !s instead 0==s
const skip6=i=>s=>(t,c)=>{if(!t){let t;s(0,(s,f)=>{!s?(t=f,c(s,f)):1==s&&0<i?(i--,t(1)):c(s,f)})}};            // remove `skipped` variable (decrement until 0 instead increment to max)
const skip7=i=>s=>(t,c)=>{if(!t){let t;s(0,(s,f)=>{!s?(t=f,c(s,f)):1==s&&0<i--?t(1):c(s,f)})}};                // glueing `i?(i--...` to `i--(...`
const skip8=i=>s=>(t,c)=>{if(!t){s(0,(s,f)=>{!s?(t=f,c(s,f)):1==s&&0<i--?t(1):c(s,f)})}};                      // reuse `start` variable in inner scopes as it is only used once "far out"
const skip9=i=>s=>(t,c)=>{if(!t)(s(0,(s,f)=>{!s?(t=f,c(s,f)):1==s&&0<i--?t(1):c(s,f)}))};                      // () instead {} to make it more `expression` friendly
const skipA=i=>s=>(t,c)=>{!t&&(s(0,(s,f)=>{!s?(t=f,c(s,f)):1==s&&0<i--?t(1):c(s,f)}))};                        // `!t&&` instead `if(!t)`
staltz commented 2 years ago

Cool code golfing! I would accept some PRs for this, as long as we don't do minification of variable names by hand. i.e. keep names as they were and code formatting/indentation as well.

PEZO19 commented 2 years ago

@staltz Okay! I'll do that! :) Would you prefer to see the evolution of the code step by step in a branch or you're only interested in the end result? Also: are you ok with omitting variables? (Reusing a variable for multiple "purposes" if context makes that possible.)

PEZO19 commented 2 years ago

@staltz This one? Shall I change index.js or make a new file?

const skip = max => source => (start, sink) => {
  !start &&
    (source(0, (t, d) => {
      !t ? (
        start = d,
        sink(t, d)
      ) :
      t == 1 &&
        0 < max-- ?
          start(1) :
      sink(t, d)
  }))
};
staltz commented 2 years ago

@PEZO19 I'd like to at least keep the ifs, unless they are a leaf if.