Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions EVALUATION-README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# WebStreams Alternative - Complete Evaluation

This directory contains a comprehensive evaluation of the proposed WebStreams implementation as an alternative to the current asPipes implementation.

## Files

1. **EVALUATION-SUMMARY.md** - Executive summary with quick comparison table and key insights (recommended starting point)
2. **EVALUATION.md** - Detailed technical analysis with architecture considerations and semantic alignment
3. **webstreams-evaluation.test.js** - 17 automated tests validating the comparison

## Quick Answer

**Should we replace the current implementation with the WebStreams alternative?**

**No.** Keep the current implementation.

## Key Reasons

1. **Performance**: Current implementation is 6-10x faster
2. **Semantics**: Current matches F# pipeline operator (single values), WebStreams uses arrays
3. **Features**: Current has higher-order composition, object integration, parameterized functions
4. **Alignment**: WebStreams serves a different use case (batch stream processing)

## Test Results

All tests pass:
- ✅ 48 original tests
- ✅ 17 new evaluation tests
- ✅ Performance benchmarks
- ✅ Feature comparison tests
- ✅ Semantic alignment tests

## What is the WebStreams Alternative?

A micro implementation (~27 lines) using the Web Streams API that was shared in the GitHub issue. While interesting, it:

```javascript
// WebStreams approach (array-based)
const $ = pipe();
$(x => x + 1) | $(x => x + 1);
await $.run([1, 2, 3]); // [3, 4, 5]
```

vs. current implementation (single-value based):

```javascript
// Current approach (single value)
const { pipe, asPipe } = createAsPipes();
const inc = asPipe(x => x + 1);
const p = pipe(1);
p | inc | inc;
await p.run(); // 3
```

## Recommendation

Keep the current implementation because:
- It better matches the project's stated goal: "model the semantics of the proposed |> pipeline operator"
- The F# pipeline operator works with single values, not arrays
- Superior performance and feature set
- Already has comprehensive tests and documentation
- Stream support already available via stream.js module

## Possible Future Enhancement

If batch processing becomes important, consider adding an optional `batch()` function to the current implementation rather than replacing it entirely.

## How to Run Tests

```bash
# Run all original tests
npm test

# Run evaluation tests
node --test webstreams-evaluation.test.js
```

## References

- Original issue: Micro alternative using streams
- F# Pipeline Operator: https://github.com/tc39/proposal-pipeline-operator
- Web Streams API: https://streams.spec.whatwg.org/
104 changes: 104 additions & 0 deletions EVALUATION-SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# WebStreams Alternative Evaluation - Executive Summary

## Question
Could the proposed WebStreams implementation be a better alternative to the current asPipes implementation?

## Answer
**No.** The current implementation should be retained.

## Quick Comparison

| Criterion | Current Implementation | WebStreams Alternative | Winner |
|-----------|----------------------|----------------------|--------|
| **Performance** | Baseline | 6-10x slower | ✅ Current |
| **Single Value Processing** | ✅ Native | ❌ Array-only | ✅ Current |
| **F# Pipeline Semantics** | ✅ Matches | ❌ Different paradigm | ✅ Current |
| **Higher-Order Composition** | ✅ Full support | ❌ Not supported | ✅ Current |
| **Parameterized Functions** | ✅ `add(10)` syntax | ⚠️ Closures only | ✅ Current |
| **Object Integration** | ✅ `asPipe(Math)` | ❌ Manual wrapping | ✅ Current |
| **Code Size** | ~96 lines | ~27 lines | ✅ WebStreams |
| **Standard APIs** | Custom | Web Streams | ✅ WebStreams |

## Key Insights

### 1. Different Design Goals
- **Current**: Models F# pipeline operator (|>) for single values
- **WebStreams**: Batch stream processing for collections

These are fundamentally different use cases.

### 2. Performance
The current implementation is **6-10x faster** in typical usage scenarios based on automated benchmarks.

### 3. Feature Richness
The current implementation supports critical features absent from WebStreams:
- Pipes that return pipes (higher-order composition)
- Clean parameterized function syntax
- Object method integration
- Single value focus (matching |> semantics)

### 4. Semantic Alignment
The project's stated goal is to "model the semantics of the proposed |> pipeline operator." The F# pipeline operator works with single values:

```fsharp
value |> transform1 |> transform2
```

**Current implementation** ✅:
```javascript
const result = pipe(value);
result | transform1 | transform2;
await result.run(); // Returns single value
```

**WebStreams alternative** ❌:
```javascript
const $ = pipe();
$(transform1) | $(transform2);
await $.run([value]); // Returns array
```

## Recommendation

**Keep the current implementation.** It better serves the project's goals:
1. ✅ Matches F# pipeline operator semantics
2. ✅ Significantly better performance
3. ✅ More features and flexibility
4. ✅ Already has comprehensive tests and documentation
5. ✅ Stream support already available via stream.js module

## Possible Enhancement

If batch processing becomes important, consider adding an **optional** batch mode:

```javascript
const { pipe, asPipe, batch } = createAsPipes();

// Current: single value (keep as primary)
const p1 = pipe(5);
p1 | inc | double;
await p1.run(); // 12

// Optional: batch processing (new)
const p2 = batch([1, 2, 3, 4, 5]);
p2 | inc | double;
await p2.run(); // [4, 6, 8, 10, 12]
```

This approach:
- Preserves current single-value semantics
- Adds batch capability when needed
- Reuses existing transformation functions
- Maintains API consistency

## Evidence

Comprehensive evaluation completed with:
- ✅ Performance benchmarks (see comparison.js)
- ✅ Feature comparison tests (see webstreams-evaluation.test.js)
- ✅ Semantic alignment analysis (see EVALUATION.md)
- ✅ All existing tests still passing (48 tests)

## Conclusion

The WebStreams approach is interesting for educational purposes and demonstrates creative use of Web APIs, but it doesn't align with asPipes' design goals and underperforms the current implementation. The current implementation should be retained as it better models the F# pipeline operator and provides superior performance and features.
179 changes: 179 additions & 0 deletions EVALUATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
# Evaluation: WebStreams Alternative Implementation

## Summary
This document evaluates the proposed WebStreams implementation against the current asPipes implementation to determine if it could be a better alternative.

## Background
The issue proposes a micro alternative using WebStreams that is described as "not as robust" but with an "interesting" implementation approach. The proposed code is ~27 lines compared to the current ~96 lines in index.js.

## Key Findings

### Performance Comparison

Performance benchmarks show the current implementation is consistently faster:

| Test Case | Performance Ratio |
|-----------|------------------|
| Single value operations | ~6-10x faster |
| Batch processing | Comparable (use case dependent) |

**Verdict:** The current implementation is significantly faster for single-value operations, which is the primary use case.

### Feature Comparison

| Feature | Current Implementation | WebStreams Alternative |
|---------|----------------------|----------------------|
| Single value processing | ✅ Core design | ❌ Array-only |
| Batch array processing | ✅ Via Promise.all or stream.js | ✅ Native |
| Reusable transformations | ✅ Full support | ⚠️ Limited |
| Higher-order composition | ✅ Pipes returning pipes | ❌ Not supported |
| Parameterized functions | ✅ Clean syntax `add(10)` | ⚠️ Via closures only |
| Object method integration | ✅ `asPipe(Math)` | ❌ Manual wrapping |
| Async generator support | ✅ Via stream.js | ❌ Not applicable |
| F# pipeline semantics | ✅ Matches closely | ❌ Different paradigm |

**Verdict:** The current implementation has significantly more features and flexibility.

### Code Quality

**Current Implementation:**
- Well-structured with clear separation of concerns
- Comprehensive test suite (48 tests)
- Extensive documentation in README
- Handles edge cases (issue #9 - objects with run methods)
- Type coercion semantics carefully implemented

**WebStreams Alternative:**
- Simpler code (~27 lines)
- Uses standard Web Streams API
- Less code to maintain
- Limited test coverage in proposal
- Missing edge case handling

### Semantic Alignment

**Current Implementation:**
The stated goal from the README is to "model the semantics of the proposed |> pipeline operator" which operates on **single values**, not arrays:

```javascript
// F# style pipeline (single value)
value |> transform1 |> transform2
```

The current implementation matches this perfectly:
```javascript
const result = pipe(value);
result | transform1 | transform2;
await result.run();
```

**WebStreams Alternative:**
Operates on **arrays/collections**, which is a different paradigm:
```javascript
$.run([value1, value2, value3])
```

This is closer to stream processing libraries like RxJS or Highland, not the F# pipeline operator.

### Use Case Analysis

**Current Implementation Best For:**
- Single value transformations (matching |> semantics)
- Complex composition patterns
- Integration with existing APIs/libraries
- Synchronous and asynchronous workflows
- Functional programming patterns

**WebStreams Alternative Best For:**
- Batch processing of collections
- Streaming large datasets
- Scenarios requiring backpressure
- Browser environments with native streams support

## Architecture Considerations

### Current Implementation Strengths:
1. **Composability**: Pipes can return pipes, enabling powerful abstractions
2. **Flexibility**: Works with single values, promises, and async generators
3. **Semantics**: Closely models the proposed F# pipeline operator
4. **Ecosystem**: Can integrate with existing stream.js module
5. **Developer Experience**: Parameterized functions, object method wrapping

### WebStreams Alternative Strengths:
1. **Simplicity**: Smaller codebase
2. **Standard API**: Uses Web Streams (WHATWG standard)
3. **Backpressure**: Built-in via streams
4. **Memory**: Potentially better for very large datasets

### Current Implementation Weaknesses:
1. More complex codebase
2. Custom implementation (not using standard APIs)
3. Learning curve for Symbol.toPrimitive approach

### WebStreams Alternative Weaknesses:
1. Array-only processing (doesn't match |> semantics)
2. No higher-order composition
3. Limited parameter support
4. Missing key features (asPipe with objects, etc.)
5. Different mental model from F# pipelines

## Recommendations

### Primary Recommendation: **Keep Current Implementation**

**Rationale:**
1. **Performance**: 6-10x faster in typical usage scenarios
2. **Features**: Significantly more capabilities and flexibility
3. **Semantic Alignment**: Matches F# pipeline operator goals
4. **Completeness**: Already has extensive tests and documentation
5. **Ecosystem**: Stream support already available via stream.js

The WebStreams approach is interesting but solves a **different problem** (batch stream processing) than what asPipes aims to solve (F# pipeline operator semantics).

### Optional Enhancement: Add Batch Processing Mode

Consider adding an optional batch processing mode to the current implementation:

```javascript
// New feature idea (optional)
const { pipe, asPipe, batch } = createAsPipes();

const inc = asPipe((x) => x + 1);
const double = asPipe((x) => x * 2);

// Single value mode (current)
const p1 = pipe(5);
p1 | inc | double;
await p1.run(); // 12

// Batch mode (new, optional)
const p2 = batch([1, 2, 3, 4, 5]);
p2 | inc | double;
await p2.run(); // [4, 6, 8, 10, 12]
```

This would:
- Keep the current single-value semantics as primary
- Add batch processing when needed
- Use the existing transformation functions
- Maintain consistent API and behavior
- Provide choice based on use case

## Conclusion

The WebStreams implementation is an interesting exploration of using native browser APIs, but it:
1. **Doesn't align** with the project's stated goals (F# pipeline semantics)
2. **Underperforms** the current implementation (6-10x slower)
3. **Lacks features** critical to the asPipes value proposition
4. **Serves a different use case** (batch processing vs single-value pipelines)

**Recommendation: Do NOT replace the current implementation.** The current approach better serves the project's goals and provides superior performance and features. If batch processing becomes a priority, consider adding it as an optional enhancement to the existing implementation rather than replacing the core design.

## Testing Evidence

A comprehensive evaluation test suite was created with 17 automated tests, demonstrating:
- Performance benchmarks showing current implementation is faster
- Feature coverage showing current implementation supports more use cases
- Semantic alignment showing current matches F# pipeline goals better

All tests pass. Test file: `webstreams-evaluation.test.js`.
Loading