CHUTE - Chain Functions AND Native Methods
 Language: Vanilla JavaScript
Version 1: 2024-11-27
  Version: 2024-11-29
     View: Normal
 Download: Normal (8KB)
           Minified (2KB)
   Notice: By and © Greg Abbott 2024
    Links: Log
           GitHub
  License: 
CHUTE 
License for the chute.js script and minified variants.

The MIT License

Copyright (c) 2024, Greg Abbott

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Example use

demonstrate_scope()
function demonstrate_scope(){
  let seed = [1, 2, 4]
  const result = chute(seed,//start chute with a value
    /*optional first functions to send data through:*/
    pusher(8)
  )//The main functionality starts after calling chute.
//==============================================================
  //CALL METHODS
  // Method calls native to the current data work as normal.
  .map(double)
  .filter(greater_than(4))
  .reverse//Methods that need no argument need no parentheses.
  //Chute has basic handling of non-returning methods.
  .push(6)//push returns the updated data, not the new length.
  .forEach(x=>log('Side Effect: '+x))
//==============================================================
  //CALL GLOBAL FUNCTIONS
  //------------------------------------------------------------
  //VIA NAMED CALLS (method chain style)
  //".f_name()" calls any global function named "f_name".
  //If chute sees no arguments, it sends the data as argument 1.
  //".f_name()" or ".f_name" act like "data = f_name(data)"
  .wrap_in_array
  //Chute keeps the return value for any next step in the chain.
  .get_index(0)//".f(arguments)" == "data = f(arguments)(data)"
  //------------------------------------------------------------
   //VIA ".do" CALLS (these can work as Sub-chains)
  //".do" sends data through 1+ functions in a single call.
  //".do(f1,f2)" sends data to f1, then f1's return to f2.
  .do(pusher(32),pusher(64)/*etc*/)
  //Nameless ".do" calls work the same way.
  (pusher(80),pusher(128,250))
  //sub-chains use reduce so mimic nested calls.
  //".do(f1,f2)" works like "f2(f1(x))"
//==============================================================
  //CALL NON-GLOBAL / SCOPED FUNCTIONS
  //Chute's built in methods can receive non-global functions.
  (local_unary)
  //Chute remembers each named non-global function it receives,
  //and then makes it available to call by name later:
  .local_unary
  //------------------------------------------------------------
  //The call inside the call below returns an unnamed function.
  (local_push(80))
  //Chute sends the data through the function, keeps the return,
  //but discards the function with no name to call it by later.
//==============================================================
  //ORDER
  //Chute evaluates what to do per named call.
  //Chute checks the called name against names of other actions.
  //If nothing matches Chute returns an error.
  //Otherwise it calls the first item found with the same name.
    //A method built in to Chute. (more details below)
    //A native method of the current data.
    //A global function
    //A named non-global function (from some previous call)
//==============================================================
  //TOKENS
  //------------------------------------------------------------
  //ARGUMENT PLACEHOLDER (For ".name" calls to functions)
  //The 'x' property of the chute name holds this token.
  //If Chute sees this token, it swaps it for the current data.
  .placeholder_test('Normal Argument',/*Token:*/chute.x)
  //The above call equates to: "data = fn(argument 1, data)".
  
  //When a method-style function call has arguments and no token
  //Chute expects the function call to return another function.
  //It then sends the data to that function, keeping the result.
  //e.g. ".fn(a,b,c)" == "data = fn(arguments)(data)"
  //------------------------------------------------------------
  //CURRENT VALUE TOKEN
  //the '_' property of the chute name holds the current data.
  //use it for inline expressions
  .do(chute._[3])//Property access
  .do((chute._+64))//Arithmetic
  .do({data:chute._})//Object literals
  .do(`Answer is "${chute._.data}"`)//Template literals
  .do(4>2?extract_quoted:chute._)//Ternary (with function call)
  .do([48,64,80,chute._])//Array literal
//==============================================================
  //BUILT IN METHODS
  //------------------------------------------------------------
  //LOG
  //The built in ".log()" method logs the current data.
  .log
  //If it receives any arguments, it logs these before the data.
  .log('Log with note')
  //------------------------------------------------------------
  //TAP
  //".tap" calls a built in method that takes one function.
  //Chute sends the data to function, ignoring any return value.
  //Even so, a ".tap" function might mutate data directly.
  .tap(mutate)
  .log('Post-tap data')
  //------------------------------------------------------------
  //IF (basic)
  //'.if' calls a built in method that takes 2 arguments.
  //Argument 1 takes a value [e.g. "is_saturday()"]
  //or a function. Chute calls any function and uses the result.
  //".if(fn)" == "if(fn(data))"
  
  //A truthy argument 1 value lets Chute evaluate Argument 2
  //Argument 2 can hold: a value to replace the current data;
  //or a function to send the data through, keeping the return.
  .if(is_array,sum)
  .log('Sum: ')
  //The data remains the same when the condition proves falsey.
//==============================================================
  //IF ELSE BLOCK 
  //An object usable within ".do" calls.

  //Shape:
  //It needs an 'if' property that holds a 2 element array.
  //The other properties, else_if, and else, remain optional.
  //any "else_if" property holds an array, of 2 element arrays.
  //any "else" property holds a value, or a function to use.
  //The block can have 'else' without 'else_if' or vice-versa.
  //The object must have no other properties.
  
  //The block returns the value of the first matching condition.
  //A matching condition may return a function to send data to.
  //In that case, Chute calls it and keeps the value it returns.
  //Without an 'else' property or a match, data stays unchanged.
  ({
    if: [is_array, 8],
    else_if: [
      [is_string, 16],
      [   Number, local_scope_x10],
    ],
    else: 41
  })
  //Chute remembers non-global functions if blocks mention too.
  .local_scope_x10
//==============================================================
  //ENDING THE CHUTE
  //a named call before the chain end needs parentheses '()'.
  .log()//[<- Design under consideration]
  ()//<- an empty call returns the data value, ending the chute.
  //From this point, method calls directly act on the data.
  .toString()
  .replace(/$/,'!')
  log(`Chute Result:`,result)
  
  function local_push(...a){return l=>(l.push(...a),l)}
  function local_unary(f){log('Local Unary');return f}
  function local_scope_x10(f){log(`LocalTenfold`);return f*10}
}
//Demo Helper Functions (Global)
function return_falsey(){return 0}
function placeholder_test(a,x){log(a,x);return x}
function extract_quoted(x){return x.replace(/.*?"(.*?)"/,'$1')}
function get_index(i){return data => data[i]}
function wrap_in_array(data){return [data]}
function pusher (...a){return l => {l.push(...a);return l}}
function log (...x){console.log(...x);return x}
function greater_than(n){ return x => x > n}
function double(x){return x * 2}
function sum(data){return data.reduce((a,b)=>a+b)}
function is_array(data){return Array.isArray(data)}
function is_string(data){return typeof data === 'string'}
function mutate (d){log('Mutate');d.length=2;return 'chuck'}