[HN Gopher] Effect - Build robust apps in TypeScript
       ___________________________________________________________________
        
       Effect - Build robust apps in TypeScript
        
       Author : PaulHoule
       Score  : 102 points
       Date   : 2024-06-14 16:20 UTC (6 hours ago)
        
 (HTM) web link (effect.website)
 (TXT) w3m dump (effect.website)
        
       | tra3 wrote:
       | I dont have an opinion on the project itself (yet), but I gotta
       | say the website and the introduction is great.
       | 
       | The section on "without effect" and "with effect" is intriguing
       | and makes me want to dig in more.
       | 
       | I used to be of the opinion that things should just stand on
       | their own but lately I've accepted the fact that the hind brain
       | visual appeal is important.
        
       | epolanski wrote:
       | This is essentially Scala's ZIO for Typescript.
       | 
       | The huge advantage is Typescript obviously, a much more popular
       | language to spread the same ideas.
       | 
       | I've been happily using the effect ecosystem for years and can't
       | but say I am super happy to do so.
       | 
       | It does require some patience to learn and it's not really super
       | friendly to people that never really took the time to learn
       | typescript, but if you did it's such a pleasure to write code for
       | Node/Demo/Browser/Workers.
        
       | neya wrote:
       | I was going to comment something snarky, but damn, it does look
       | good! As someone with Elixir background, it looks really
       | attractive to me and I was able to grasp the problem statement in
       | just 10 seconds. Impressive presentation format!
        
       | AirMax98 wrote:
       | Buried pretty deep in the docs: "Effect's major unique insight is
       | that we can use the type system to track errors"
       | 
       | This website should probably start with this. Instead it's a
       | bunch of like, modern synergy-ese.
        
         | epolanski wrote:
         | From the main page:
         | 
         | https://postimg.cc/sBQB37h6
         | 
         | There's lots to Effect beyond error handling, I can imagine it
         | can seem a bit overwhelming.
         | 
         | The great thing is that you can cherry pick what you need.
        
           | prophesi wrote:
           | Maximum Type Safety and Error Handling doesn't tell the
           | interesting bit that it uses the type system to track errors.
           | (And I really don't know what Maximum Type Safety is supposed
           | to mean in the first place)
        
             | epolanski wrote:
             | I can imagine, how would you improve it though?
             | 
             | Concurrency, runtime, tracked errors, functional
             | composition, dependency injection, etc there's lots of
             | problems that the ecosystem solves.
             | 
             | It's hard to say which of these features is particularly
             | more important.
        
               | adamwk wrote:
               | I guess to me it looks like yet another monads for JS
               | library. I get its a marketing page but marketing to
               | whom? Maybe there's a better document to share on HN that
               | shows how Effect improves those features?
        
         | galaxyLogic wrote:
         | In plain (ES6) JavaScript I can extend the class Error with my
         | own error-classes and then extend those further. So I can
         | create a different error-class for ever conceivable error. I
         | can also use "instanceof" to infer whether an error is an
         | instance of any (recursive) subclass of the error-class I'm
         | testing for.
         | 
         | How does EffectTS improve upon that?
        
           | riwsky wrote:
           | It makes the errors that a function can fail with part of
           | that function's type.
        
           | mind-blight wrote:
           | Just reading the docs, but it looks like it forces these
           | errors into compile-time checks instead of runtime checks. It
           | makes you a lot more confident that code that compiles with
           | run correctly and without error.
           | 
           | Seems like it's mostly trying do similar things to what
           | Haskell or StandardML does, but in Typescript.
        
           | eyelidlessness wrote:
           | The other answers you've gotten are right, and this is mostly
           | just expanding on their points. But speaking for a me, this
           | is how I view the value proposition:
           | 
           | With Effect (and ideas like it), you can treat error
           | conditions as normal expressions, without special control
           | flow. You can reason about the error conditions the same way
           | you reason about the successful data flow. You can treat your
           | own Error subclasses just like any other value type, with the
           | same kind of conditional logic in the same flow. And you can
           | do that without looking deeper into the call stack to
           | understand where errors come from, because errors flow
           | exactly where values flow.
           | 
           | Because errors and values are in the same flow, you can
           | compose things in ways you couldn't (or at least wouldn't
           | typically) with separate error control flow. You can build
           | complex logic by composing simpler fundamentals, in a way
           | that's more declarative and self-documenting. Your error
           | instanceof checks can be combined with map/filter/whatever
           | else just like you might use with success values, and they
           | can be mixed so that recoverable error conditions are
           | expressed even more like success conditions.
           | 
           | If you're into types, all of the same benefits apply at the
           | type system level. You don't have to care about types to
           | enjoy these benefits. If you don't care now but embrace types
           | in the future, you'll just get more of the same benefits in
           | that hypothetical future.
        
         | mind-blight wrote:
         | That was my first thought too. I thought it was a new web
         | framework at first, then an ETL pipeline library. It took me
         | about 5 pages of reading before I found an actual description,
         | and I only knew what it was trying to accomplish from the code
         | example because I've done functional programming before
        
         | PaulHoule wrote:
         | You can but should you in some particular way?
         | 
         | It's often said that checked exceptions are the worst mistake
         | in Java and I believe that.
         | 
         | To be specific consider the case of a class that implements
         | FetchSomeData which fetches some data (could just as well be a
         | function with parameters encoded inside it as a closure) which
         | could come from many sources such as                  * hard
         | coded in the class        * read from a file        * fetched
         | via http        * fetched from Postgresql or CouchDB or ...
         | 
         | the one thing these have in common is they can fail in very
         | different ways. If you want to take encapsulation seriously
         | here the right way to do it is to expose certain semantics of
         | the error such as                  * Could the user avoid this
         | problem by changing their inputs?        * What do we tell the
         | end user?  What do we tell the sysadmin?        * Is it likely
         | that this problem will clear up if we retry the request in five
         | minutes?        * Is attempting to use this object likely to
         | cause worsening data corruption
         | 
         | and such. In Java we get the very week beer of people creating
         | a large number of exceptions like
         | FailureinSubsystemSeventeenException and sprinking thousands of
         | methods with throws clauses and doing a lot of error-prone
         | catching and rethrowing to please the compiler but if you
         | really are serious about error handling you know you could get
         | a RuntimeException because something wrote a[15] on an array
         | that has only 12 elements and you still need to catch those
         | anyway.
         | 
         | So far systems that use something like Either[Result|Exception]
         | aren't based on an ontology of failure but my experience is
         | that code written like that tends to have the same problems you
         | have with checked exceptions: harried by the compiler people
         | often omit error handling code to the maximum extent that they
         | can. From working with both kinds of code bases I'd say that
         | code bases worked on exceptions have the glass half full and
         | handle errors properly more than they do improperly, these
         | monad-based systems are the other way around.
        
       | hn_throwaway_99 wrote:
       | I haven't finished the whole intro video yet, but if anyone from
       | Effect sees this, curious why y'all went with your own schema
       | validation library? I've worked on a bunch of projects across a
       | bunch of companies and everyone seems to have standardized on
       | Zod. Any thought on making Zod pluggable here?
        
         | epolanski wrote:
         | Zod is a great library, you can use it with Effect without any
         | issues!
         | 
         | Schema is a more advanced library though that goes beyond
         | parsing, it supports both encoding and decoding, also supports
         | more complex types.
         | 
         | Check the docs of effect/schema to get a taste.
        
           | srhtftw wrote:
           | Indeed both Zod and Schema are great with Effect.
        
       | noname120 wrote:
       | Ok this is cool and all but where do I put my breakpoints now
       | when something unexpected happens and I need to debug production
       | code?
        
         | epolanski wrote:
         | You do it the same way you do it in any JS application.
        
           | noname120 wrote:
           | Well you can't because now you declaratively represent the
           | execution flow, and the actual execution is implicit. If you
           | put any breakpoint in there it will break during the
           | declaration of the execution flow rather than when the
           | corresponding logic actually gets executed:
           | Http.request.get(`/todos/${id}`).pipe(
           | Http.client.fetchOk,           Http.response.json,         )
        
             | epolanski wrote:
             | You put the breakpoint where you run it with Effect.runX
             | then.
        
               | noname120 wrote:
               | Yeah, and then it won't step you through your code but
               | through unintelligible abstractions and Effect internals.
               | Good luck debugging.
        
               | epolanski wrote:
               | That's a typical issue when debugging the JS ecosystem,
               | it's not really an effect problem per se, albeit since
               | Effect comes with a runtime there's way more noise as you
               | noticed. It doesn't help your example has only library
               | code to test.
        
               | johnfn wrote:
               | It is actually Effect-specific, because Effect modifies
               | control flow in JS. For instance, if you pipe 3 functions
               | in Effect, you'll have to step through the pipe function
               | to get from the execution of the first to the execution
               | of the next. Working around this adds overhead every time
               | you debug your application. In vanilla JS, `a = foo(); b
               | = bar(a); c = baz(b)` might not be as pretty, but it is
               | trivial to step through.
        
               | galaxyLogic wrote:
               | Plus if you are able to debug it somehow, and discover
               | the bug in your code, can you fix it right there in your
               | debugger?
               | 
               | Or do you need to figure out what is the place in your
               | original unprocessed source-code that corresponds to the
               | code you see in the debugger, and then switch from the
               | debugger to edit the original file and then rebuild and
               | rerun everything and see if that fixes the problem?
               | 
               | I wonder if that is a solved problem in plain TypeScript
               | either?
        
         | ivanjermakov wrote:
         | This is why it was hard for me to work on complex functional
         | programming projects: execution flow is not obvious and you
         | can't just debug/print in the middle of the function.
         | 
         | Repl is often sufficient, but not in cases where you need to
         | pass some convoluted data as input.
        
           | riwsky wrote:
           | A fair criticism. To Effect's credit, though, their
           | OpenTelemetry integration reports usable spans (i.e. stack
           | trace matching the mental model, not the trampolined event
           | loop) and associates the logs with them appropriately.
        
       | jauntywundrkind wrote:
       | Shout-out to my coworker who highlighted this banger from a
       | wonderfully down & dirty review of effect-ts,
       | 
       | > _If you 're on drugs, check out Effect.loop. Someone smart
       | asked "How can we make a for loop deterministic and palatable to
       | Functional Programmers?" they then smoked crack and did a good
       | job._
       | 
       | https://www.linkedin.com/pulse/quick-thoughts-effect-ts-jess...
       | 
       | The review is discursive & dense; I wish we saw more hot take
       | rampages through codebases like this.
        
       | EugeneOZ wrote:
       | Reminds me of Result in Rust.
        
       | srhtftw wrote:
       | I've used Effect-ts for a little under a year.
       | 
       | It's great for workflows with clear points in the logic where
       | things should happen and it provides more sophisticated (i.e.
       | powerful but harder to master) tools like Fibers and Streams
       | which allow you to reason about failure cases of reactive
       | asynchronous operations. In many cases it offers a clear path out
       | of callback-hell that is more reliable than promises and
       | async/await.
       | 
       | However while the Effect-ts docs are getting better and may be ok
       | for people with a good knowledge of functional programming and
       | Typescript, they are nowhere near the quality they need to be for
       | those who don't. People looking for examples online will get
       | frustrated because Effect API churn over the past three years has
       | made many old posts and articles obsolete. Old github repos won't
       | work out of the box. And you better be comfortable with codemods
       | if you haven't frozen your Effect-ts version.
       | 
       | Fortunately the Effect-ts Discord channel is full of friendly and
       | helpful people and the Effect team provides high quality
       | assistance to people who ask for it. It makes me sad this
       | treasure trove of information is trapped in Discord where search
       | is of little value.
       | 
       | A good book or collection of high quality examples of how to use
       | Effect-ts with de-facto standard frameworks like React could help
       | its adoption grow significantly.
        
         | mind-blight wrote:
         | Do you know if people have tried distributing execution across
         | multiple machines using Effect? That's one of the big
         | advantages of fully immutable code execution imo, and I'm
         | really interested if anyone has succeeded in leveraging Effect
         | for that purpose.
        
           | srhtftw wrote:
           | I don't believe anyone has done that yet in the way I think
           | you're thinking for Effect-ts - i.e. something which would
           | give effect execution a kind of location-transparency.
           | 
           | That said, the person who built the ZIO project which
           | inspired Effect-ts is now working on an exciting project
           | called Golem Cloud1 which aims to provide durable and
           | reliable location-transparent execution of Wasm programs.
           | 
           | Mike Stonebraker's DBOS2 looks to provide something similar
           | for Typescript.
           | 
           | 1 https://github.com/golemcloud
           | 
           | 2 https://www.dbos.dev
        
         | fellowniusmonk wrote:
         | I really just wish groups would train a llama 3 model and have
         | AI assisted answers pulled directly from source code. Like if
         | this project made https://docs.effect.website and I could query
         | it directly secretllama.com style.
        
           | PaulHoule wrote:
           | That would require the models to have real reasoning ability
           | as opposed to looking up preformed answers on Stack Overflow.
           | 
           | Study after study shows that many evaluations of LLMs are
           | deceptive because they evaluate them on questions that were
           | answered in the training set. On the other hand, this is
           | exactly why they can do so well on medical board exams
           | because you don't learn to pass the medical board by
           | predicting what medical treatments _should_ work according to
           | first principles but instead by remembering which medical
           | treatments have been _proven_ to work.
           | 
           | For that matter I've long wanted a conventional search engine
           | which can be specialized for a particular software project I
           | am working on: for instance I might be using JDK 17 and JooQ
           | version 3.15 and I only want to search those versions of
           | docs. It shouldn't be hard to look at the POM file to figure
           | these versions out. I have the exact same problem with
           | Javascript where I have to work with code that uses different
           | versions of react-router, bootstrap, MUI, etc. The idea LLM
           | coding assistant should do the same.
        
             | benced wrote:
             | I just asked GPT 4o to do the following and it succeeded:
             | 
             | > Write me JS code that generates a random list of 10 prime
             | numbers, takes the sine of each (as radians), then takes
             | the cosine of each of those values (as degrees) and then
             | adds 5 to each value
             | 
             | I assure you, this task is not on Stackoverflow (because
             | nobody would ever be deranged enough to want to do this)
             | but GPT 4o could do it!
             | 
             | Admittedly, this shows only a very rudimentary reasoning
             | ability but it demonstrates _some_. The idea that LLMs are
             | just a slight evolution of search indexes is demonstrably
             | false.
        
               | doomroot wrote:
               | This takes 0 reasoning. You've given it an exact map to
               | follow to the answer.
        
               | PaulHoule wrote:
               | LLMs are also good at tasks that are roughly "linear" in
               | the sense that a group of input tokens corresponds to a
               | group of output tokens and that translation moves from
               | left to right.
               | 
               | In a hard programming problem, for instance, you have a
               | number of actions that have to take place in a certain
               | dependency order that you could resolve by topological
               | sort, but in a problem like the above one bit of English
               | corresponds to one bit of Python in basically the same
               | order. Similarly if it couldn't translate
               | take the sine of...
               | 
               | to                   Math.sin(x)
               | 
               | because the language didn't already have a sin function
               | it would have to code one up. Similarly, translating
               | between Chinese and English isn't really that hard
               | because it is mostly a linear problem, people will accept
               | some errors, and it's a bit of an ill-defined problem
               | anyway. (Who's going to be mad if one word out of 50 is
               | wrong whereas getting one word wrong in a program could
               | mean it doesn't compile and delivers zero value?)
               | 
               | LLMs can do a bit of generalization beyond full text
               | search, but people really underestimate how much they
               | fake reasoning by using memory and generalization and how
               | asking them problems that aren't structurally close to
               | problems in the training set reveals their weakness.
               | Studies show that LLMs aren't robust at all to the
               | changes in the order of parts of problems, for instance.
               | 
               | The biggest problem people have reasoning about ChatGPT
               | is that they seem to have a strong psychological need to
               | credit it with more intelligence than it has the same way
               | we're inclined to see a face inside a cut stem. It can do
               | an awful lot, but it cheats pervasively and if you don't
               | let it cheat it doesn't seem as smart anymore.
        
       | chaostheory wrote:
       | Why would you want to simulate threads instead of using something
       | like actors instead?
        
       | darkest_ruby wrote:
       | This is nothing but just TaskEither that has existed (among
       | others incredibly useful things for years) in fp-ts.
        
         | epolanski wrote:
         | It's a good intuition. Indeed you can see it as
         | ReaderTaskEither (albeit on steroids).
         | 
         | The fp-ts author is also an effect maintainer and recommends
         | effect for new projects. fp-ts is not dead, but it's core goals
         | (providing Haskell/PureScript-like types and type classes) have
         | been mostly achieved.
         | 
         | Effect is an ecosystem with different goals from fp-ts.
        
         | mixedCase wrote:
         | This is for all intents and purposes, fp-ts 3. The projects
         | essentially merged, Giulio Canti is working on it and fp-ts 2
         | is renaming methods to ease the transition to it.
        
       | sureIy wrote:
       | Oof this looks awful and I pity anyone who started projects on
       | this. It's so far removed from regular JavaScript you should have
       | just written your own language. If/when this project goes under,
       | whole codebases need to be binned.
        
         | diob wrote:
         | It's one of those things that's great for a solo dev. But I'd
         | cringe having to come into a codebase like this. And then have
         | to learn all the pitfalls, and onboard folks and have them
         | learn the pitfalls. . .
         | 
         | I also am a bit weirded out by the Effect.Effect<unknown,
         | HttpClientError>. . . What is unknown?
        
           | eropple wrote:
           | `unknown` is a standard type in TypeScript for "it could be
           | anything and you don't know what it is, so you need to
           | introspect it and assert a type if you want to use it". (As
           | opposed to `any`, which is "it could be anything but you _don
           | 't_ have to introspect it to use it, you're just opting out
           | of the type system".)
           | 
           | This is not "normie" TypeScript but I wouldn't characterize
           | it as particularly scary.
        
             | diob wrote:
             | I know what unknown is but why isn't it typed? I don't see
             | it as an improvement from the well typed thing on the left.
        
               | a-morales wrote:
               | It isn't typed since the response from the http call can
               | be anything. It could be a Todo, a generic success
               | wrapper that contains a Todo, or maybe it was recently
               | changed to return a Checklist instead of a Todo. Since
               | the response can be anything we have two options, either
               | make the type be any or unknown. any is an escape hatch
               | typescript provides that allows anything, so if you
               | access a field that doesn't exists on the response you'll
               | get a runtime exception. With unknown typescript doesn't
               | let you do anything with the value until you figure out
               | the type. In this example the next step would be to try
               | to decode that unknown into a Todo, or fail with some
               | error[1]. This guarantees that the object your code
               | expects is actually what you are expecting both at
               | compile time and runtime.
               | 
               | Given the goal of effect is to provide type safe tracking
               | of a codebase; having an any breaks that assumption,
               | while unknown forces you to add validation to keep it
               | type safe.
               | 
               | 1. https://github.com/Effect-
               | TS/effect/blob/main/packages/schem...
        
         | seabrookmx wrote:
         | Reminds me of old React codebases that used mobx :')
        
       | gotts wrote:
       | I liked the plot comment "Complexity(Lower is better)" because in
       | Javascript community it seems like not so obvious to many people
        
       | eiriklv wrote:
       | A bit on the side:
       | 
       | Stream processing of data is nicer using a declarative approach.
       | Though I've found that describing control flow is often more
       | intuitive using an imperative approach. It's a shame that redux-
       | saga got tied to react/redux, because the idea of generators and
       | declarative effects are universal - useful on both backend and
       | frontend.
        
       | RussianCow wrote:
       | At various points in my career, I've seriously considered writing
       | an effects library very similar to this one because the benefits
       | in a sufficiently complex system are clear to me. But ultimately,
       | JavaScript doesn't have the functional primitives to make a
       | library like this very ergonomic. In particular, the lack of
       | syntactic sugar for function composition (e.g. a "pipe" operator
       | such as |> as seen in functional languages) means every operation
       | needs to be composed via method calls and argument passing, which
       | adds a ton of visual noise that detracts heavily from the
       | conceptual simplicity of the library.
       | 
       | If your project, as a whole, benefits heavily from a functional
       | style of programming, you are likely better off using one of the
       | many functional programming languages that compile to JavaScript,
       | like ReScript, OCaml, F#, or Scala.
        
       | johnfn wrote:
       | Hm, maybe I'm not seeing the point here, or maybe I'm just too
       | old and crotchety. But it seems to me that Effect provides two
       | things:
       | 
       | 1. A way to have strongly-typed errors
       | 
       | 2. A standard library, like a better HTTP library and better
       | concurrency primitives.
       | 
       | I'm not particularly sold on 1) in isolation. If you want
       | strongly typed errors, you can use vanilla TS: just use `type
       | Result<T> = { type: 'result'; data: T } | { type: 'error';
       | message: string }` as a return type and you're basically there,
       | modulo an if statement on the calling side.
       | 
       | On the other hand, 2) seems a bit more compelling. I do think
       | it's true that JS could use a better standard library. But the
       | cost of the standard library is that I now have to write my code
       | in this extremely unusual way, and that seems like a bit too high
       | of a cost to me. Effect.forEach (instead of for...of),
       | Effect.runPromise (instead of await), Effect.andThen (instead
       | of... nothing?) have, to my eyes, significant penalties in
       | readability, debuggability and maintainability.
        
         | steve_adams_86 wrote:
         | Something which gets me about these Effect methods is that very
         | similar things can be accomplished with generators. I assume
         | this is all generators under the hood. I personally would
         | rather just write that code because once you're familiar with
         | it, it's really not that difficult to work with.
         | 
         | I agree that a wrapper around generators is nice if it suits
         | the situation (like async/await), and abstracting them is
         | useful at times, but I'm not sure I'd want to pull in an entire
         | library for it.
         | 
         | One thing, the Effect.runPromise and similar methods which seem
         | like overkill are probably providing quite a bit more utility
         | and reliability than it appears. It might offer good cleanup
         | guarantees as well.
         | 
         | I should add that this is the equivalent of a hot take. If I
         | looked closer I'm sure I'd change my mind, but I'm old and
         | crotchety like you.
        
       | apozem wrote:
       | The operator chains remind me of RxJS. Which is great! I love
       | reactive programming. But speaking as someone who spent two years
       | teaching RxJS to a team of frontend newbies, the learning curve
       | is _brutal_.
       | 
       | (Please do not reply with "I found RxJS easy". Good for you.)
        
         | nwienert wrote:
         | I once used RxJS for a dev tool platform, we had a use case
         | where we had to take a tree-like structure of user data and
         | recursively resolve all the async nodes.
         | 
         | Took it to the RxJS discord after a couple days of pounding my
         | head on it. One of the creators was there and was super
         | helpful.
         | 
         | We went back and forth on the problem at least 6 times each,
         | with new attempts. He tried quite a few variations, but none
         | ever worked.
        
         | LoganDark wrote:
         | > (Please do not reply with "I found RxJS easy". Good for you.)
         | 
         | What about "what makes it hard"? I've never tried RxJS before,
         | so I'm curious to know where the learning curve is.
        
       | fire_lake wrote:
       | Without do-notation this is kind of dead on arrival no?
        
       | nielsbot wrote:
       | What I really want JS/TS is one of my favorite features about
       | Swift:
       | 
       | Throwing functions in Swift don't throw traditional exceptions
       | (please someone up the stack catch this). Throwing an error in
       | Swift actually returns an Error to the calling function instead
       | of that function's normal result type. Swift also enforces that
       | when you call code that might "throw" (return an error instead of
       | a result) you either handle the error or "rethrow" it.
       | 
       | In TS I use this pattern:
       | 
       | `type ErrorThrowingFunction = <T,>() => T|Error`
       | 
       | But it's not nearly as nice to use in TS since it's not a
       | language feature.
        
       | ozuly wrote:
       | For me personally, I find a functional programming to be the most
       | productive when working on a project as a solo dev or as part of
       | a very small team that I've hired. But the downside is that it's
       | much harder to onboard devs without experience in functional
       | programming. It takes a while before they can be productive when
       | introduced to a codebase that's mostly written in a functional
       | style -- one that's written in Effect for example.
       | 
       | I recently started a company and was tempted to use Effect as I
       | felt I would be able to build faster and higher quality than
       | Standard TypeScript, but decided against it since I felt that I
       | would pay the price when it came to grow the team. So far the
       | decision has paid off.
       | 
       | I did indulge myself a little bit though and do make heavy use of
       | remeda and ts-pattern across my code.
        
       ___________________________________________________________________
       (page generated 2024-06-14 23:02 UTC)