Hacker News new | past | comments | ask | show | jobs | submit login

I love Zod, but recently I've been converting to Effect's Data and Schema modules.

Previously I liked a combination of Zod and ts-pattern to create safe, pattern matching-oriented logic around my data. I find Effect is designed far better for this, so far. I'm enjoying it a lot. The Schema module has a nice convention for expressing validators, and it's very composable and flexible: https://effect.website/docs/guides/schema/introduction

There are also really nice aspects like the interoperability between Schema and Data, allowing you to safely parse data from outside your application boundary then perform safe operations like exhaustively matching on tagged types (essentially discriminated unions): https://effect.website/docs/other/data-types/data#is-and-mat...

It feels extremely productive and intuitive once you get the hang of it. I didn't expect to like it so much.

I think the real power here is that these modules also have full interop with the rest of Effect. Effects are like little lazy loaded logical bits that are all consistent in how they resolve, making it trivial to compose and execute logic. Data and Schema fit into the ecosystem perfectly, making it really easy to compose very resilient, scalable, reliable data pipelines for example. I'm a convert.

Zod is awesome if you don't want to adopt Effect wholesale, though.






I've had a very similar experience, and have been slowly moving from zod and ts-rest to @effect/schema and @effect/platform/HttpApi as well as migration to Effect Match from ts-pattern. There is a learning curve but its a pretty incredible ecosystem once you are in it for a bit.

I think the real turning point was typescript 5.5 (May 2024). The creator of typescript personally fixed a bug that unlocked a more natural generator syntax for Effect, which I think unlocks mainstream adoption potential.

https://twitter.com/MichaelArnaldi/status/178506160889445172... https://github.com/microsoft/TypeScript/pull/58337


I feel like Effect is today's Ramda. So cool but it's going to be regretted by you and people coming after you in few years. Me and my team reverted to more stupid code and we are happier.

My experience with fp-ts and io-ts was that we quickly got to a point where the team was divided into a small group of people usually with postgraduate CS degrees who really understood it, and then everyone else who saw it as black magic and were afraid to touch it.

Nowadays I’d rather rely on libraries that don’t require a phd to use them properly.


You just gave me a flashback to scalaz https://github.com/scalaz/scalaz

> Me and my team reverted to more stupid code and we are happier.

This is 100% how to write more reliable software. We are in the process of reducing our TS dependencies to effectively just express and node-postgres and everything is becoming infinitely easier to manage.


Yes, all true - apart from treating errors as values and including them function signatures... That should simply be something every modern language should ship with

I have never written any code in Go, but increasingly I am writing my TS in the style I hear Go code is written in. Very procedural, very verbose. All of the fancy indirection you get with coding everything with higher order functions just makes the program impossible to debug later. Procedural style programming lends itself to debugging, and I definitely am so dumb I need to debug my own programs.

I may simply be too dumb for lots of fancy functional programming. I can barely understand code when reading one line and statement at a time. Reading functions calling functions calling functions just makes me feel like gravity stopped working and I don't know which way is up. My brain too small.


I agree in some contexts. Kind of like Rust, I see a place for more durable code that's harder to reason about in some cases.

I wouldn't use Effect for a lot of things. For some things, I'm very glad to have it. One thing Effect has going for it that Ramda didn't is that it's much less abstract and it's quite a bit more opinionated about some more complex concepts like error handling, concurrency, or scheduling.

Kind of like state machines. You shouldn't use them for everything. For some things, it's a bad idea not to (in my opinion).

Then of course subjectivity is a factor here. Some people will never like conventions like Effect, and that's fine too. Just write what feels right.


I think going all-in on Effect in its current state is not something I'd do. However there's a subset of its functionality that I'm currently replicating with a bunch of different libraries: ts-pattern, zod, some lightweight result / option wrapper like ts-belt, etc. Pretty much trying to pretend I'm writing ML / OCaml. Having those all in one package is quite convenient. Add in TypeScript's the much needed story around retry / observability / error handling—I see why people lean into it.

Having experience with ZIO / FP in Scala, I'm a bit biased in seeing the value of Effect systems as a whole, but taking on the burden of explaining that mental model to team members and future maintainers is a big cost for most teams.


Coming from a ReasonML / OCaml codebase (frontend react), I'm seeing a lot to love with the pattern matching and sum types. Zod is already one of my favorites (coming from https://github.com/glennsl/bs-json).

Is 'retry / observability / error handling" something that comes from Effect?


That's right, Effect lifts all types to a lazily-evaluated common type and provides combinators to work with that type, similar to RxJS with Observables and its operators.

Retrying[0], observability[1], and error handling[2] are first-class concerns and have built-in combinators to make dealing with those problems quite ergonomic. Having these features is a non-starter for any serious application, but unfortunately, the story around them in the TypeScript ecosystem is not great—at least as far as coherence goes. You often end up creating abstractions on top of unrelated libraries and trying to smash them together.

I'm a big fan of ReasonML / OCaml, and I think the future of TypeScript will involve imitating many of its code patterns.

[0] https://effect.website/docs/guides/error-management/retrying

[1] https://effect.website/docs/guides/observability/telemetry/t...

[2] https://effect.website/docs/guides/error-management/expected...


Yes it's quite sad.

I liked the idea of Ramda until I saw code bases that where using it for everything.

I'm doing JS for over a decade now and I couldn't understand a thing.


Don't judge Effect based on Rambda they have completely different objectives, Rambda focused a lot on abstractions similar to fp-ts, Effect focuses almost exclusively on concrete implementations

It's not the library per se, it's that the library will require all-or-nothing buy in from your entire development team for it to be useful and persistently maintained, similar to how Rambda affected a codebase and a development team.

It's the same effect as adding async code to Python or Rust, suddenly the entire team and the entire codebase (and often dependency choices) must adhere to it.


One of the things I like about Effect is that incremental adoption is easy, and you really don't have to use it everywhere.

You can choose to make a single flow in your application an effect program. Or you can base most of your functions around it. It's really up to you how and where it's used. If you want to use an effect within non-effect code, that's easy to do, too.

You can think of effects like values. The value is obtained by executing the effect. Until it's called, the effect can be placed anywhere, in any function, in a generator, within promises, etc. Once you need its value, you execute it. It's compatible with most code bases as long as you can execute it to get the value. It's really up to the developer how portable they want their effects to be.


I understand, but this is very much like promises in JS. You can pass promises around without awaiting their return values too but async and/or promise code inevitably infects the rest of the codebase. I've never really seen anyone just have promises walled off in one specific module where the rest of the codebase sticks to callbacks.

Passing Effects around will similarly infect the entire codebase, resulting in the entire dev team who interacts with it needing to buy in. Limiting the output of Effects to a single module owned by one zealot dev undermines having it around in the first place and it'll get removed and replaced as soon as that person leaves or gives up the fight.


Effect is not only much easier to get productive on than Ramda, but it provides an entire ecosystem.

Our team is full effect from two years and juniors can pick it and start working on it with ease.


Having references to work from is essential. Their documentation doesn’t do as good of a job demonstrating that as it could, in my opinion.

I’ve found that with most libraries - they always provide toy foobar style examples assuming it’ll make them approachable, but in reality, instead makes it impossible to understand the practical way to use it in real world settings.

That is quite of an hard problem to solve.

Solutions like effect are easier to appreciate as your application starts growing in complexity beyond simple todo apps.

Solutions like effect/schema are easier to appreciate as soon as you start needing complex types, encoding/decoding, branded types and more.

I am quite confident that effect will keep growing in popularity steadily and eventually grow.

It took more than 5/6 years for TypeScript or React to start getting spread around the JS community. Effect is here to stay and I'm confident it will eventually be adopted by plenty of developers.


Reminds me of Animal / Cat / Dog examples.

For the love of god just use User / RegisteredUser / GuestUser and other abstractions that have some basis in the real world.


that is certainly a possibility

How did you find learning Effect? The sales pitch sounds great, but when I went through the docs it seemed pretty confusing to me. I’m sure there are reasons for the everything but I couldn’t grok it. In particular, I’m thinking of the Express integration example.[0] I look at that and think, I need all that just to create a server and a route? What’s the benefit there? I’m hesitant to buy into the ecosystem after looking at that. I want to like it, though.

[0] https://effect.website/docs/integrations/express


Hands down, the best (free, no email) resource to learn Effect is here https://www.typeonce.dev/course/effect-beginners-complete-ge..., as opposed to the docs. The link referenced gives a holistic view of how to incorporate it.

I've been following the author Sandro Maglione for quite a while and am on his email list, he's great. He wrote fpdart which I've used and now he seems to be all in on Effect, with XState.

That’s just another level. Only in five pages of explanations we come to something that is basically:

  await fetch(…)
    .catch(e =>
      new CustomError(…))
But with a wrapped-promise and flatmap nonsense for “better error handling”.

FP always goes out of the way to avoid using the language it operates in and to criticize the ways of doing something it just imagined. As if it wanted to stay as noble from unwashed peasants as it could, but has to do the same job to keep existing.

how to test () => makePayment()? (from the link)

You don’t. You test constituents like request body generation and response handling. It’s inside. You can’t test your Effect-version of this code neither. It’s a strawman.


Yeah, looking at that example feels like jumping straight into the deep end of the pool. I think it helps going through a tutorial that breaks down the why of each piece. I really liked this tutorial on it: https://www.typeonce.dev/course/effect-beginners-complete-ge...

Some core things from Effect though that you can see in that Express example:

* Break things down into Services. Effect handles dependency injection, that's typed, for services so you can easily test them and have different versions running for testing, production, etc. * Fibers for threaded execution * Managing resources to make sure they're properly closed with scope

I think a lot of these things though often aren't truly appreciated until you've had something go wrong before or you've had to build a system to manage them yourself. *


But I feel like I've worked with massive systems with a lot going on where nothing has gone wrong that this sort of thing specifically would solve it. I think it would just increase learning curve and make people make other types of mistakes (business logic or otherwise) because it's so much less readable and understandable. I've seen similar libraries used in the past that have caused much more worse bugs because people misunderstand how they exactly work.

Yeah, this looks like the tutorial I needed. Thanks.

I agree, some of there examples are a little overly complicated by their quest to be hyper composable. In fact they should probably remove that example. I am currently using it with Remix, and using their @effect/platform package to produce a simple web handler (request: Request) => Response (thank remix for heavily promoting the adoption of web standards).

I fully agree parts of the ecosystem are complex, and likely not fully ready for broad adoption. But I do think things will simplify with time, patterns will emerge, and it will be seen as react-for-the-backend, the de facto first choice. effect + schema + platform + cluster will be an extremely compelling stack.


I agree about the turning point. Things have improved dramatically. And I know it probably doesn't feel the same for tons of people, but I love to see generators being used in every day code.

The learning curve just about turned me away from it at the start, but I'm glad I stuck with it.

I think learning Effect would actually teach a lot of people some very useful concepts and patterns for programming in general. It's very well thought out.


Why not write your code in F# and compile it to TypeScript using Fable [1]?

This way you can use native language features for discriminated unions, functional pipelines, and exhaustive pattern matching to model your domain instead of shoe-horning such functionality into a non-ML language!

Model your domain in F#, consume it in Python or C# backends and TypeScript frontends. The downside is needing to know all of these languages and run times but I think I'd rather know F# and the quirks with interacting with TypeScript than a library like Effect!

[1] https://fable.io



I've tried compile-to-JS languages before but their big weaknesses are:

1. Debugging can become quite a pain. Nobody likes debugging generated code.

2. You don't get to use libraries and tools from the enormous JavaScript ecosystem.

3. Eventually you'll find some web feature that they haven't wrapped in your language and then you're in for FFI pain.

In the end I found Typescript was good enough that it wasn't worth dealing with those issues.


Fable is still very much integrated with the runtime so there’s an expectation to handle those bullet points with inline annotations.

You can build an entire application in F# and compile to JS but another option is compiling to TS and calling that F# code from your TS app. I/O and views and whatnot are written in TS and the domain model is in F#. The entire model could be nothing but pure functions and resolve to a single expression!


This is so cool! I’ll have to try it out soon. Thanks!

I like the functionality, but the verbosity of the API makes me want to immediately ignore it. I feel like zod nailed the usability part.

Schema is a much powerful tool than Zod. Zod is merely a parser, while Schema has a decoder/encoder architecture.

Like Java, you can write Haskell in any language but that doesn't mean it's always a good idea.

Which is why our principle with Effect is NOT to port Haskell. For example we don't use Typeclasses which are big in haskell, we lean heavily on variance which is not in Haskell. It's an ecosystem though to write production-grade TypeScript, not Haskell in TypeScript

I’d love to hear more stories of people using Effect in production codebases.

It looks very similar in its ideas to fp-ts (in the “let’s bring monads, algebraic types etc to typescript” sense).

But I did hear from teams that embraced fp-ts that things kinda ground to a halt for them. And those were category theory enthusiasts that were very good scala devs so I’m sure they knew what they were doing with fp-ts.

What happened was that the typescript compile time just shot into minutes, for a moderately sized micro-service, without anything externally heavy being introduced like you could on the frontend.

It just turned out that Typescript compiler was not so great at tracking all the inferred types throughout the codebase.

So wonder if things have improved or effect uses types more intelligently so that this is not an issue.


> It looks very similar in its ideas to fp-ts (in the “let’s bring monads, algebraic types etc to typescript” sense).

It's the next version of fp-ts, developed by the same people, AFAIK


Effect reminds me more of something like NestJS - essentially an end to end application framework that takes over your whole application.

Rather disappoint to see something like this being plugged as an alternative to something like zod which is a nice library that stays in its corner and has a nice fixed scope to it's functionality.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: