Skip to content

Overload pipe function to enable pipe syntax in lambda functions for library APIs#13

Open
Copilot wants to merge 5 commits into
mainfrom
copilot/explore-utility-for-libraries
Open

Overload pipe function to enable pipe syntax in lambda functions for library APIs#13
Copilot wants to merge 5 commits into
mainfrom
copilot/explore-utility-for-libraries

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Oct 9, 2025

Overview

This PR overloads the pipe function to enable the use of pipe syntax inside lambda functions, making it perfect for library configuration APIs. This addresses the question posed in this Hacker News comment about whether asPipes could support this pattern:

grid.columns.name.format(v => v | trim | truncate | bold)
form.fields.name.validate(v => v | trim | required | email)

Solution

The pipe function now detects when it's called with a function argument and automatically creates a pipeable function, eliminating the need for a separate pipeFn helper. This provides a cleaner, more intuitive API:

import { createAsPipes } from 'aspipes';

const { asPipe, pipe } = createAsPipes();

// Define transformation/validation functions
const trim = asPipe((s) => s.trim());
const required = asPipe((s) => {
  if (!s) throw new Error('Required');
  return s;
});
const email = asPipe((s) => {
  if (!s.includes('@')) throw new Error('Invalid email');
  return s;
});

// Use in library APIs with clean pipe syntax
form.fields.email.validate = pipe((v) => v | trim | required | email);

// Call like a normal async function
await form.fields.email.validate('  user@example.com  '); // → 'user@example.com'
await form.fields.email.validate('invalid'); // → throws Error('Invalid email')

Implementation

The pipe function now checks the type of its argument:

const pipe = (x) => {
  // If x is a function, create a pipeable function
  if (typeof x === 'function') {
    return async (...args) => {
      const ctx = { v: args[0], steps: [], token: null };
      const token = {
        [Symbol.toPrimitive]: () => (stack.push(ctx), 0),
        async run() {
          return ctx.steps.reduce((p, f) => p.then(f), Promise.resolve(ctx.v));
        },
      };
      ctx.token = token;
      x(token, ...args.slice(1));
      return await token.run();
    };
  }
  
  // Regular pipe behavior for values (original behavior preserved)
  const ctx = { v: x, steps: [], token: null };
  const token = {
    [Symbol.toPrimitive]: () => (stack.push(ctx), 0),
    async run() {
      return ctx.steps.reduce((p, f) => p.then(f), Promise.resolve(ctx.v));
    },
  };
  ctx.token = token;
  return token;
};

Benefits for Library Authors

  • Simpler API: Single pipe function instead of separate pipe and pipeFn
  • More intuitive: The same function handles both values and function-based pipelines
  • Clean API: Users can configure validation/transformation using intuitive pipe syntax
  • Composability: Reusable operations combine seamlessly
  • Type-safe: Each operation receives the output of the previous one
  • Async-ready: Handles promises and async operations automatically
  • Error handling: Errors propagate naturally through the pipeline

Changes

  • Overloaded pipe function in index.js to detect function arguments
  • Removed pipeFn export (cleaner API with just pipe, asPipe, and take)
  • Updated 6 test cases to use overloaded pipe (all 50 tests passing)
  • Updated README with API documentation explaining overloaded behavior
  • Updated library-example.js to use pipe instead of pipeFn
  • Updated hn-comment-example.js to use pipe instead of pipeFn

Example Use Cases

Form validation:

form.fields.email.validate = pipe((v) => v | trim | required | email);
form.fields.name.validate = pipe((v) => v | trim | required | minLength(3));

Data formatting:

grid.columns.name.format = pipe((v) => v | trim | truncate(20) | bold);
grid.columns.date.format = pipe((v) => v | parseDate | formatDate('YYYY-MM-DD'));

Data transformation:

api.transform = pipe((v) => v | parseJson | extractField('data') | normalize);

Backward Compatibility

✅ Zero breaking changes - all existing tests continue to pass
✅ Fully backward compatible with existing pipe(value) and asPipe usage
✅ Cleaner exports: { pipe, asPipe, take } instead of { pipe, asPipe, take, pipeFn }

Original prompt

Could you figure out if this is possible (from a conment in Hacker News)
tacone 5 hours ago | prev | next [–]

I know that it's wrong but I love it.
I am wondering if it could be useful for libraries:
grid.columns.name.format(v => v | trim | truncate | bold)
form.fields.name.validate(v => v | trim | required | email)
reply


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 3 commits October 9, 2025 20:36
…ocumentation

Co-authored-by: irony <395843+irony@users.noreply.github.com>
Co-authored-by: irony <395843+irony@users.noreply.github.com>
Co-authored-by: irony <395843+irony@users.noreply.github.com>
@irony
Copy link
Copy Markdown
Owner

irony commented Oct 9, 2025

@copilot could we overload the pipe function instead of having a separate pipeFn?

@irony irony marked this pull request as ready for review October 9, 2025 20:40
Copilot AI review requested due to automatic review settings October 9, 2025 20:40
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements a pipeFn helper function that enables pipeline-style syntax for library configuration, directly addressing the Hacker News comment asking about utility for expressions like v => v | trim | truncate | bold. The implementation adds a minimal 7-line function that wraps arrow functions containing pipe expressions, making them callable as regular async functions.

  • Added pipeFn helper function to enable pipe syntax within arrow functions for library APIs
  • Comprehensive test suite with 6 new test cases covering various use scenarios
  • Documentation updates with examples and new example files demonstrating real-world usage

Reviewed Changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
index.js Adds the core pipeFn implementation and exports it
test.js Adds 6 comprehensive test cases covering string formatting, validation, numeric operations, arrays, async operations, and library simulation
README.md Updates documentation with pipeFn API details, usage examples, and library use case section
library-example.js Comprehensive example file demonstrating various pipeFn use cases including grid formatting and form validation
hn-comment-example.js Direct implementation of the exact syntax from the Hacker News comment
PIPEFN-SUMMARY.md Implementation summary document explaining the feature, benefits, and test coverage

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Copilot AI changed the title [WIP] Investigate potential utility for library functions Add pipeFn helper to enable pipe syntax in lambda functions for library APIs Oct 9, 2025
Copilot AI requested a review from irony October 9, 2025 20:41
Co-authored-by: irony <395843+irony@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Oct 9, 2025

@copilot could we overload the pipe function instead of having a separate pipeFn?

Done! Overloaded the pipe function to detect when it's called with a function argument. Now you can use pipe((v) => v | trim | upper) instead of needing a separate pipeFn. The implementation checks typeof x === 'function' and creates a pipeable function in that case, while maintaining the original behavior when called with values. All tests updated and passing. (commit: will be in next message)

Copilot AI changed the title Add pipeFn helper to enable pipe syntax in lambda functions for library APIs Overload pipe function to enable pipe syntax in lambda functions for library APIs Oct 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants