[HN Gopher] Show HN: FP-pack - Functional pipelines in TypeScrip...
___________________________________________________________________
Show HN: FP-pack - Functional pipelines in TypeScript without
monads
Hi HN, I built fp-pack, a small TypeScript functional utility
library focused on pipe-first composition. The goal is to keep
pipelines simple and readable, while still supporting early exits
and side effects -- without introducing monads like Option or
Either. Most code uses plain pipe/pipeAsync. For the few cases
that need early termination, fp-pack provides a SideEffect-based
pipeline that short-circuits safely. I also wrote an "AI agent
skills" document to help LLMs generate consistent fp-pack-style
code. Feedback, criticism, or questions are very welcome.
Author : superlucky84
Score : 13 points
Date : 2026-01-03 15:00 UTC (8 hours ago)
(HTM) web link (github.com)
(TXT) w3m dump (github.com)
| superlucky84 wrote:
| One thing I'd especially like feedback on is the SideEffect
| approach.
|
| It's intentionally not a monad, and I'm curious how others feel
| about this trade-off compared to Option/Either in real-world
| TypeScript codebases.
| cobbal wrote:
| Is early termination the only supported side effect? Its name
| suggests a more general capability, but I didn't see more
| examples in my (cursory) look at the readme
| superlucky84 wrote:
| Good question.
|
| Early termination is the most common use case, but it's not
| the only thing SideEffect represents. The name is
| intentionally a bit broader -- it's meant to model "effects
| where normal composition should stop".
|
| In practice, that includes things like validation failures,
| logging or notifications at pipeline boundaries, and error
| reporting or metrics. That said, the scope is deliberately
| conservative.
|
| SideEffect isn't meant to be a general-purpose effect system.
| If it were, it would quickly turn into something very close
| to a monad or effect framework, which I'm intentionally
| avoiding.
| epgui wrote:
| How is this not a monad? It might be trying really hard not to
| reify the core concept of a monad, but it seems to me like it
| ends up being essentially a complicated monad.
| superlucky84 wrote:
| It's definitely monad-adjacent.
|
| The main difference is that SideEffect isn't a compositional
| context -- there's no bind/flatMap, and composition
| intentionally stops once it appears. It's meant as an explicit
| early-exit signal in pipe-first code, not a general computation
| container.
| epgui wrote:
| Is there any advantage whatsoever to this, as opposed to a
| proper monad? I'm not seeing it.
|
| The point of monads is that they solve this exact category of
| problem in the simplest possible way.
| superlucky84 wrote:
| One more practical point is that a full monad doesn't fit
| very naturally into a pipe-first interface.
|
| Once you commit to a real monad, you need map/flatMap,
| lifting, unwrapping, and rules about staying inside the
| context across the whole pipeline. At that point, the pipe
| abstraction stops being the primary mental model -- the
| monad does.
|
| SideEffect deliberately avoids that. It keeps the pipe
| interface intact and only adds a single, explicit signal:
| "stop here". That's why it's less powerful than a monad,
| but also much simpler to integrate into existing pipe-based
| code.
| superlucky84 wrote:
| fp-pack is also intentionally scoped for everyday frontend
| developers.
|
| It tries to borrow function composition and declarative
| structure without requiring familiarity with full FP
| abstractions like monads or effect systems.
| eterps wrote:
| While I like the concept, the side-effect pattern might be
| difficult for the average developer to understand (without fp-
| pack knowledge) in a shared codebase:
|
| https://github.com/superlucky84/fp-pack?tab=readme-ov-file#s...
|
| Still, this approach is very useful for most business logic, too
| bad most programming languages don't provide a nice syntax for
| this.
| superlucky84 wrote:
| That's a fair point, and I agree.
|
| The SideEffect pattern is intentionally explicit, so without
| fp-pack context it can look unfamiliar at first. The trade-off
| is making early exits visible in the code, rather than hiding
| them in conditionals or exceptions.
|
| In practice, most code stays in plain pipe/pipeAsync.
| SideEffect is meant for a small number of boundary cases only.
|
| And I agree -- better language-level syntax for this kind of
| pattern would make it much easier to adopt.
| wk_end wrote:
| The default any return type from pipelines is kind of brutal.
| Seems like a real footgun. Is that fixable?
| superlucky84 wrote:
| That's a fair point -- it can definitely become a footgun if
| you're not careful.
|
| This happens because `runPipeResult` defaults its generic
| parameter to `R = any`. When type safety matters, the intended
| solution is to use `pipeSideEffectStrict`, which preserves all
| possible SideEffect result types as a precise union throughout
| the pipeline.
|
| The default version prioritizes ergonomics and simplicity,
| while the strict version prioritizes type safety.
|
| Also, fp-pack is still in an early stage, so the usability and
| API choices haven't been fully validated yet. That's why
| feedback like this is especially helpful in shaping the
| direction of the library.
| almosthere wrote:
| Every time I introduce something like this teams complain. RX is
| a brain antipattern
| superlucky84 wrote:
| I really relate to that.
|
| There's often a gap between what feels conceptually clean and
| what teams are actually willing to carry cognitively. Rx in
| particular tends to exceed that budget pretty quickly.
|
| That's why fp-pack is intentionally narrow -- it's closer to
| making a few control-flow cases explicit in pipe-first code
| than introducing a broad new abstraction.
| mrkeen wrote:
| Haskell does not have nulls. Java 8 introduced Options, and now
| there are nulls and Options.
|
| Please tell me you didn't just add SideEffects to a language full
| of side-effects.
| superlucky84 wrote:
| I understand the concern.
|
| The intent isn't to add more side effects to an already side-
| effectful language. It's closer to the opposite: trying not to
| handle side effects all over the place, but to surface them as
| part of a single, explicit flow.
|
| This is less about adding something like Option to a language
| without nulls, and more about making control-flow boundaries
| visible in a multi-paradigm language where effects already
| exist.
|
| It's not an attempt to pretend the language is pure, just a
| small step toward more declarative discipline.
___________________________________________________________________
(page generated 2026-01-03 23:01 UTC)