Why should I use Effect? What benefit does it provide compared to plain Typescript?
Today we learn what are the problems with native Typescript code and how Effect solves them all 🔥
We compare a plain Typescript implementation of some open source code on Github and how we can rewrite the same code to take advantage of all the benefits provided by Effect:
The function is simple, but the implementation hides some errors.
Problems with typescript
The first issue is error handling.
Both fs.existsSync and gunzip can fail, for example:
Corrupted gzip file
Impossible to access file system
Missing permissions
The implementation doesn't perform any error handling, and the return type Promise<void> does not help to understand what can go wrong.
Second problem: dependencies and tests.
Dependencies are implicit, and therefore impossible to inject and hard to test:
fs
gunzip
Implicit dependencies cannot be mocked for testing.
They also create a strong coupling of the code to a specific library, which makes refactoring way harder.
Logging.Logger is instead injected as parameter, but it needs to be provided every time you call the function (inconvenient):
There is more.
Every week I build a new open source project, with a new language or library, and teach you how I did it, what I learned, and how you can do the same. Join me and other 600+ readers.
Same implementation using Effect
Let's look at the same implementation using Effect, and how Effect solves all the above problems:
All dependencies are explicit (provided using Context + Layer)
Effect automatically keeps track of possible error directly in the return type
Effect provides logging out of the box, which can be then customized (using Logger and Layer)
FileSystem module
The Effect ecosystem provides some packages to implement common usecases (Http, FileSystem, Stream and more).
In this case we use the FileSystem module from @effect/platform.
FileSystem is an Effect wrapper around fs that handles all errors:
fs.exists returns Effect.Effect<boolean, PlatformError>, where PlatformError represent an error when checking if the file exists
Gzip module
When a module is not already present in the Effect ecosystem we can easily implement our own.
In this example we implement a Gzip module:
Inside a new Gzip.ts file we create a make function that wraps gunzip using Effect to handle errors:
Effect.asyncEffect to wrap async code that returns a callback function (gunzip)
Effect.tryPromise to catch any errors when executing gunzip
Call resume when the gunzip callback returns successfully
We then export the implementation as a module using Context.Tag:
Logger module
Logging is provided out of the box by Effect using methods like logDebug.
Later we can customize the Logger implementation and specify the log level (Error, Debug, Info and more):
Error handling
All errors are tracked automatically by Effect.
fs.exists can return a PlatformError (BadArgument or SystemError)
gzip can return a GzipError
We can catch and handle all errors using Effect.catchTags:
Layer: Dependency injection
Until now we did not provide any concrete implementation of the dependencies.
We start by defining a Layer called Live inside Gzip. This Layer exports the make implementation that we defined previously:
For FileSystem instead we use the NodeFileSystem module that provides a complete Effect implementation of fs in Effect.
We merge these 2 layers and provide them to the function using Effect.provide (dependency injection):
Running an Effect
At the end of the function we execute the function using Effect.runPromise.
This will execute all and return Promise<void> like the original function:
This is it!
Now you can go ahead and practice rewriting your own Typescript code with Effect.
As your project grows you will notice clear improvements in speed, reliability, developer experience and more 🚀
If you are interested to learn more, every week I publish a new open source project and share notes and lessons learned in my newsletter. You can subscribe here below 👇
Every week I build a new open source project, with a new language or library, and teach you how I did it, what I learned, and how you can do the same. Join me and other 600+ readers.