% js-functional-idioms.tex — currying vs partial application (fn.length, bind), IIFE (+ the
% ASI trap, async IIFE vs top-level await), arguments vs rest, tagged templates (strings/raw,
% frozen + per-call-site identity, String.raw, sql`` placeholders), generators as lazy
% sequences (zip), eval / new Function / node:vm, splice vs slice vs toSpliced, Node CLI
% odds (shebang, process.chdir), SVG createElementNS.
% Source: docs/old-notes/js/notes.md
% Checked 2026-09-25 against MDN (Template literals: tagged templates, raw, frozen + same
% strings array per call site, invalid escapes cooked = undefined, String.raw;
% Function.length: rest excluded, counts only params before the first default; arguments:
% array-like, none in arrows, sloppy simple-params aliasing, prefer rest; eval: direct vs
% indirect, (0, eval), eval?.(), Function() is global-scoped, XSS sink; Array.toSpliced:
% ES2023 change-by-copy) and nodejs.org/api/vm.html ("not a security mechanism").
% Source errors fixed here: `function func(1,2 3)` is not valid JS (parameters are names) —
% shown as rest params; the missing ')' in console.log(process.argv.shift(); the note's
% "currying(fn, ...args)" is partial application, not currying; its zip pads with undefined
% to the LONGEST array (Python's zip stops at the shortest — shown here); `new
% vm.createContext(...)` needs no `new`; the dir() helper leaves cwd changed if the callback
% throws (needs try/finally).
% Already covered elsewhere, not repeated: closures, this/call/bind/arrow this, prototypes +
% __proto__ (js-scope-closures-this, js-prototypes-objects-classes); .mjs + top-level await
% (js-types-coercion-modules, node-runtime); child_process.spawn (node-runtime).
% Build ONLY with: tools/print/print-sheet.py <this>.tex --dry-run
% @source: hiot monorepo, docs/school/sheets/javascript/js-functional-idioms.tex — the SOURCE OF TRUTH; a copy anywhere else (e.g. artur.gurgul.pro) is regenerated from it, never edited
% @labels: area=javascript kind=pattern level=core platform=web new=no round=website-notes-2026-09-25 topic=language,patterns
% @tags: currying, partial-application, function-length, iife, arguments-object, rest-parameters, tagged-templates, string-raw, generators, eval, node-vm, array-splice
\documentclass[8pt]{extarticle}
\usepackage{printup-sheet}
\usepackage{array}

\lstdefinelanguage{TSSheet}{
  morekeywords={import,from,export,default,const,let,var,function,return,async,await,if,
    else,new,true,false,null,undefined,typeof,this,for,of,in,yield,throw,try,catch,finally},
  sensitive=true, morecomment=[l]{//}, morecomment=[s]{/*}{*/},
  morestring=[b]", morestring=[b]', morestring=[b]`}
\lstdefinestyle{js}{style=printup, language=TSSheet,
  literate={=>}{{\hbox{=}\hbox{>}}}2 {>=}{{\hbox{>}\hbox{=}}}2 {===}{{\hbox{=}\hbox{=}\hbox{=}}}3
           {!=}{{\hbox{!}\hbox{=}}}2 {...}{{\hbox{.}\hbox{.}\hbox{.}}}3}

\tikzset{
  sb/.style={box, font=\scriptsize, inner sep=1.5pt, minimum height=4.6mm},
  lbl/.style={font=\tiny, text=black!75, inner sep=1pt, align=center},
  pt/.style={font=\bfseries\small, anchor=west},
  st/.style={cell, font=\ttfamily\scriptsize, fill=sheetGreen!12, draw=sheetGreen!70!black, minimum height=4.4mm},
  vl/.style={cell, font=\ttfamily\scriptsize, fill=sheetOrange!12, draw=sheetOrange, minimum height=4.4mm},
  lane/.style={font=\tiny\bfseries, anchor=east},
}
\newcommand\fat{\hbox{=}\hbox{>}}
\newcommand\bq{\textasciigrave}

\begin{document}

\sheettitle{Functional idioms — currying, IIFE, tagged templates, generators, eval}{javascript · memo}

\oneliner{Functions are values that close over their scope, so these idioms are closures with
a shape: \textbf{currying} turns \texttt{f(a,b,c)} into \texttt{f(a)(b)(c)}; \textbf{partial
application} fixes some arguments now (\texttt{bind}); an \textbf{IIFE} makes a private scope
and runs it once; a \textbf{tagged template} is a call on a template's pieces; a
\textbf{generator} is a pausable function that yields values \emph{on demand}.}

\vspace{2pt}
\noindent\begin{tikzpicture}[sheet]
  \foreach \x in {5.75,11.2} \draw[sheetGrey!40] (\x,3.1) -- (\x,-0.45);
  % ── 1 currying ──
  \node[pt] at (-0.1,2.95) {\textcolor{sheetBlue}{1} \texttt{c = curry(sum3)}; \texttt{c(1)(2)(3)}};
  \node[sb, minimum width=11mm] (a1) at (0.55,2.0) {args\\\texttt{[1]}};
  \node[sb, minimum width=11mm] (a2) at (2.2,2.0) {args\\\texttt{[1,2]}};
  \node[sb, minimum width=13mm, draw=sheetGreen!70!black, fill=sheetGreen!10] (a3) at (4.1,2.0) {\texttt{[1,2,3]}\\3 ≥ \texttt{length}};
  \draw[hot] (a1) -- node[lbl, above]{\texttt{(2)}} (a2);
  \draw[hot] (a2) -- node[lbl, above]{\texttt{(3)}} (a3);
  \node[lbl, text=sheetBrown] at (0.55,1.35) {1 < 3: return a fn};
  \node[lbl, text=sheetBrown] at (2.2,1.35) {2 < 3: return a fn};
  \node[lbl, text=sheetGreen!50!black] at (4.1,1.35) {call \texttt{sum3(1,2,3)} = 6};
  \node[lbl, anchor=west, align=left] at (-0.1,0.75) {each step is a closure over the args so far;};
  \node[lbl, anchor=west, align=left] at (-0.1,0.5) {arity comes from \texttt{fn.length}:};
  \node[lbl, anchor=west, align=left, text=sheetRed] at (-0.1,0.15) {\texttt{(a, b = 1, c)} $\to$ 1 · \texttt{(...xs)} $\to$ 0 — curry fires too early};
  \node[lbl, anchor=west, align=left] at (-0.1,-0.2) {partial: \texttt{sum3.bind(null, 10)} fixes \texttt{a} once};
  % ── 2 tagged template ──
  \node[pt] at (5.85,2.95) {\textcolor{sheetBlue}{2} \texttt{tag\bq Hi \$\{name\}, age \$\{n\}!\bq}};
  \node[lbl, anchor=west] at (5.9,2.45) {is the call \texttt{tag(strings, name, n)}:};
  \node[lane] at (7.05,1.8) {strings};
  \node[st] (s0) at (7.6,1.8) {"Hi "};
  \node[st] (s1) at (9.15,1.8) {", age "};
  \node[st] (s2) at (10.55,1.8) {"!"};
  \node[lane] at (7.05,1.15) {values};
  \node[vl] (v0) at (8.4,1.15) {name};
  \node[vl] (v1) at (9.85,1.15) {n};
  \draw[flow] (s0.east) -- (v0.north west); \draw[flow] (v0.north east) -- (s1.west);
  \draw[flow] (s1.east) -- (v1.north west); \draw[flow] (v1.north east) -- (s2.west);
  \node[lbl, anchor=west, align=left] at (5.9,0.55) {\texttt{strings.length} = values + 1 (may hold \texttt{""}) ·};
  \node[lbl, anchor=west, align=left] at (5.9,0.3) {\texttt{strings.raw} = unescaped · both \textbf{frozen} ·};
  \node[lbl, anchor=west, align=left, text=sheetBrown] at (5.9,0.0) {the \textbf{same array} every time this call site runs};
  \node[lbl, anchor=west, align=left, text=sheetBrown] at (5.9,-0.25) {$\to$ a tag can cache per template (lit-html)};
  % ── 3 generator ──
  \node[pt] at (11.3,2.95) {\textcolor{sheetBlue}{3} a generator runs on demand};
  \node[lane] at (12.15,2.3) {caller};
  \node[lane] at (12.15,1.2) {\texttt{function*}};
  \draw[sheetGrey!60] (12.25,2.3) -- (16.5,2.3); \draw[sheetGrey!60] (12.25,1.2) -- (16.5,1.2);
  \node[lbl, anchor=west] at (11.3,2.55) {\texttt{it = zip(a,b)}: nothing runs};
  \draw[hot] (12.9,2.3) -- node[lbl, left, pos=0.4]{\texttt{next()}} (13.3,1.2);
  \draw[flow, sheetGreen!60!black] (13.9,1.2) -- node[lbl, right, pos=0.3, text=sheetGreen!50!black]{\texttt{yield}} (14.3,2.3);
  \node[lbl, text=sheetGreen!50!black, anchor=west] at (14.3,2.45) {gets \texttt{[1,'a']}};
  \draw[hot] (14.9,2.3) -- node[lbl, left, pos=0.4]{\texttt{next()}} (15.3,1.2);
  \draw[flow, sheetGreen!60!black] (15.7,1.2) -- (16.1,2.3);
  \draw[very thick, sheetBlue] (13.3,1.2) -- (13.9,1.2); \draw[very thick, sheetBlue] (15.3,1.2) -- (15.7,1.2);
  \node[lbl, anchor=west, align=left] at (11.3,0.6) {runs only between \texttt{next()} and \texttt{yield}; state lives};
  \node[lbl, anchor=west, align=left] at (11.3,0.35) {in the paused frame $\to$ lazy, infinite sequences OK;};
  \node[lbl, anchor=west, align=left] at (11.3,0.1) {\texttt{break} in \texttt{for\ldots of} calls \texttt{it.return()}};
  \node[lbl, anchor=west, align=left, text=sheetBrown] at (11.3,-0.2) {coroutine detail: js-prototypes-objects-classes};
\end{tikzpicture}

\begin{multicols}{2}

\section{How it works}
\begin{itemize}\raggedright
  \item \textbf{Currying} = calls of one argument (or ``until arity''); \textbf{partial
        application} = fix some args now. The note's \texttt{currying(fn, ...args)} is
        partial application, like \texttt{f.bind(null, 10)} (which also fixes \texttt{this}).
        \texttt{fn.length} counts only parameters before the first default, no rest.
  \item \textbf{IIFE} \texttt{(function () \{ \ldots\ \})()} or \texttt{(() \fat{} \{\ldots\})()}:
        a private scope, run once — the pre-module way to avoid globals. The parentheses make
        it an \emph{expression}. \texttt{(async () \fat{} \{ await x \})()} is only needed in
        CommonJS or a classic script — ESM (\texttt{.mjs}, \texttt{"type":"module"}) has
        top-level \texttt{await}.
  \item \textbf{\texttt{arguments}}: array-like (no \texttt{map}; \texttt{Array.from} it),
        absent in arrows (they see the enclosing function's), and in sloppy mode with simple
        params it \emph{aliases} them. Use \texttt{...rest}: a real array, and explicit.
  \item \textbf{Tagged template}: \texttt{tag(strings, ...values)}. Invalid escapes are
        allowed: cooked \texttt{undefined}, raw kept. \texttt{String.raw\bq C:\textbackslash
        new\bq} keeps the backslash. Uses: \texttt{sql\bq\bq} placeholders, \texttt{html\bq\bq},
        \texttt{css\bq\bq}, \texttt{gql\bq\bq}.
  \item \textbf{Running strings as code}: \emph{direct} \texttt{eval(s)} sees the local scope
        (XSS sink, defeats optimisers and minifiers); \emph{indirect} \texttt{(0, eval)(s)} /
        \texttt{eval?.(s)} and \texttt{new Function('a', 'return a+1')} see only globals.
        \texttt{node:vm} runs code with a contextified object as its global (writes land on
        that object).
\end{itemize}

\section{Example}
\begin{lstlisting}[style=js]
const curry = fn => function curried(...args) {
  return args.length >= fn.length
    ? fn.apply(this, args)
    : (...more) => curried.apply(this, [...args, ...more]);
};
const sum3 = (a, b, c) => a + b + c;
curry(sum3)(1)(2)(3); curry(sum3)(1, 2)(3);   // 6, 6
const add10 = sum3.bind(null, 10);            // add10(1, 2) = 13

const sql = (strs, ...vals) => ({             // no string concat:
  text: strs.reduce((q, s, i) => q + '$' + i + s), values: vals });
sql`SELECT * FROM t WHERE id = ${id}`;
// { text: 'SELECT * FROM t WHERE id = $1', values: [id] }
\end{lstlisting}

\columnbreak

\begin{lstlisting}[style=js]
function* zip(...arrs) {               // stops at the SHORTEST
  const n = arrs.length ? Math.min(...arrs.map(a => a.length)) : 0;
  for (let i = 0; i < n; i++) yield arrs.map(a => a[i]);
}
[...zip([1, 2, 3], ['a', 'b'])];     // [[1,'a'], [2,'b']]
function sumAll(...nums) {           // not func(1,2 3)
  return nums.reduce((s, x) => s + x, 0); }
function inDir(dir, work) {          // chdir is PROCESS-wide
  const prev = process.cwd(); process.chdir(dir);
  try { return work(); } finally { process.chdir(prev); } }
\end{lstlisting}

\section{Arrays — mutate or copy}
\raggedright\texttt{a.splice(start, n, ...items)} \textbf{mutates}: removes \texttt{n} from \texttt{start}
(negative = from the end), inserts, returns the removed: on \texttt{[1,2,3,4,5,6,7]},
\texttt{splice(-5, 5)} returns \texttt{[3,4,5,6,7]} and leaves \texttt{[1,2]}. \texttt{slice} copies a range;
\texttt{toSpliced}/\texttt{toSorted}/\texttt{toReversed}/\texttt{with} (ES2023) copy —
use them for React/Redux state. \texttt{shift()} removes the first element.

\section{Node odds}
\texttt{\#!/usr/bin/env node} + \texttt{chmod +x} + \texttt{package.json} \texttt{"bin"}
makes a CLI. SVG from JS needs \texttt{createElementNS(svgNS, 'rect')} with the SVG
namespace URI — plain \texttt{createElement('rect')} makes an HTML element that never renders.

\section{Interview traps}
\begin{itemize}\raggedright
  \trap{Currying a function with defaults or \texttt{...rest}: \texttt{fn.length} is too
        small, so it fires early — pass the arity explicitly.}
  \trap{IIFE after a line with no \texttt{;} — \texttt{const a = b} then \texttt{(function
        () \{\})()} parses as \texttt{b(function()\{\})()}. Lead with \texttt{;}.}
  \trap{Tag values are raw values, not escaped: a \texttt{html} tag must escape them
        itself — that is the whole point of tagging.}
  \trap{Calling \texttt{node:vm} a sandbox — Node's docs: ``not a security mechanism. Do not
        use it to run untrusted code.''}
\end{itemize}

\section{Remember}
\textbf{Curry: one arg at a time · partial: some now · IIFE: scope now · tag: a call on
the pieces · generator: on demand.}

\section{Likely questions}
\begin{enumerate}\raggedright
  \item Curry vs partial? — one-arg chain until arity vs pre-filled args.
  \item Why a frozen, reused strings array? — the tag caches per call site.
  \item Why avoid \texttt{eval}? — caller's privileges, slow, unminifiable.
\end{enumerate}

\end{multicols}

\noindent{\footnotesize\color{sheetGrey}\textit{Related:} js-scope-closures-this (closures,
\texttt{bind}) · js-prototypes-objects-classes (generators as coroutines) · js-async ·
js-types-coercion-modules (ESM, top-level await) · node-runtime (\texttt{child\_process})}

\end{document}
