Forget the Asynchronous let effect do it for you: เวลาเราเขียน Typescript function มันจะแยกกันชัดเจนไปเลยว่า function ที่เราสร้างต้องการให้เป็น Asynchronous ด้วย keyword async เวลาเราใช้ Effect ก็ไม่ต้องไปสนใจตรงนี้แล้ว เขียนแบบ Synchronous ไปได้เลย เดี๋ยว Effect runtime จัดการให้เอง
เวลาเอา functions หลายๆตัวมาทำงานต่อกัน code เราจะอ่านได้ง่ายมากๆ
Retry machanism: ถ้าหากว่า Effect เกิด Fail เราสามารถให้ Effect ทำงานใหม่อีกรอบ อีกกี่รอบก็กำหนดได้ โดยเว้นช่วงเวลาห่างกันเท่าไร ก่อนจะทำ retry ครั้งถัดไปก็กำหนดได้
ตัวอย่างเช่น มี Effect ที่ทำการดึง data จาก api ผ่าน fetch api แต่ว่าได้ error 500 Internal server error เราก็ให้ Effect ทำ retry เฉพาะส่วนนี้ได้ ไม่กระทบส่วนอื่นๆ ซึ่งการจะทำอะไรแบบนี้ได้ มันไม่ได้ง่ายเลยถ้าเราไม่มีตัวช่วย
pipe function(แทน pipe operator): Effect มี pipe function มาให้ใช้ ตรงนี้ผมชอบมาก เราสามารถเอา return value จาก function ก่อนหน้าส่งมาให้เป็น argument ใน function ถัดไปได้เลย
มี Immutable data structureArray, HashMap, HashSet, Trie etc etc พร้อม Composable API ที่ใช้งานง่ายมากขึ้น ถ้าใครเคยใช้ lib immutable, lodash ก็ใช้แทนได้เลย
Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
@param ― value A JavaScript value, usually an object or array, to be converted.
@param ― replacer A function that transforms the results.
@param ― space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
stringify(
const newUser: User
newUser))
11
}
12
catch (error) {
function(localvar)error: unknown
13
var console: Console
The console module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
A global console instance configured to write to process.stdout and
process.stderr. The global console can be used without importing the node:console module.
Warning: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O for
more information.
Example using the global console:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s','world');
// Prints: hello world, to stdout
console.error(newError('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name ='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
const out =getStreamSomehow();
const err =getStreamSomehow();
const myConsole =new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s','world');
// Prints: hello world, to out
myConsole.error(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
Prints to stderr with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()).
const code =5;
console.error('error #%d', code);
// Prints: error #5, to stderr
console.error('error', code);
// Prints: error 5, to stderr
If formatting elements (e.g. %d) are not found in the first string then
util.inspect() is called on each argument and the
resulting string values are concatenated. See util.format()
for more information.
@since ― v0.1.100
error(
function(localvar)error: unknown
error)
14
}
15
}
เมื่อดูที่ function register() เราจะเห็นว่ามันจะ return Promise<User> แต่ไม่ได้บอกว่าอาจจะเกิด Error นะ
เราก็เลยต้องใช้ try-catch block เข้ามาดักจับ Error ที่อาจจะเกิดขึ้น
ทีนี้เมื่อเราดูต่อที่ type ของ ตัวแปร error ใน catch block จะเห็นว่า type มันคือ unknown
หนึ่งในวิธีนั้นก็จะเป็นวิธีที่ใช้ Discriminated Unions Type
หลักการก็คือเราจะให้ function ที่มันอาจจะเกิด Error ได้ ให้มัน return Error as a value แทนที่จะ throw new Error()
แล้วจะบังคับให้คนที่เอา function เหล่านี้ไปใช้งานต่อต้องจัดการ handle error ซะก่อน
มาทำให้ function ที่อาจจะเกิด Error ทำการ return Error as a value กัน
เรามาทำ Discriminated type กันก่อน 👇
1
type
type Ok<A>={
type:"Ok";
value: A;
}
Ok<
function(typeparameter)AintypeOk<A>
A>={
2
type:"Ok"
type:"Ok"
3
value: A
value:
function(typeparameter)AintypeOk<A>
A
4
}
5
6
type
type Err<E>={
type:"Err";
error: E;
}
Err<
function(typeparameter)EintypeErr<E>
E>={
7
type:"Err"
type:"Err"
8
error: E
error:
function(typeparameter)EintypeErr<E>
E
9
}
10
11
type
type OkOrErr<A, E>= Ok<A>| Err<E>
OkOrErr<
function(typeparameter)AintypeOkOrErr<A, E>
A,
function(typeparameter)EintypeOkOrErr<A, E>
E>=
type Ok<A>={
type:"Ok";
value: A;
}
Ok<
function(typeparameter)AintypeOkOrErr<A, E>
A>|
type Err<E>={
type:"Err";
error: E;
}
Err<
function(typeparameter)EintypeOkOrErr<A, E>
E>
ส่วน function ที่เป็นมาจาก lib อื่นๆ หรือ function อะไรก็แล้วแต่ที่เราคุมไม่ได้อะ ให้เราเขียน Wrapper มาครอบมันซะ ถ้ามันจะมีปัญหาก็ให้ปัญหามันอยู่แค่ในนั้น แบบนี้ 👇
1
// assume hashPassword function from lib like bcrypt or argon2
Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
@param ― value A JavaScript value, usually an object or array, to be converted.
@param ― replacer A function that transforms the results.
@param ― space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
stringify(
const newUserResult: Ok<User>
newUserResult.
value: User
value),{
ResponseInit.status?: number
status:200})
78
}
79
catch (
function(localvar)error: unknown
error) {
80
var console: Console
The console module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
A global console instance configured to write to process.stdout and
process.stderr. The global console can be used without importing the node:console module.
Warning: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O for
more information.
Example using the global console:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s','world');
// Prints: hello world, to stdout
console.error(newError('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name ='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
const out =getStreamSomehow();
const err =getStreamSomehow();
const myConsole =new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s','world');
// Prints: hello world, to out
myConsole.error(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
Prints to stderr with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()).
const code =5;
console.error('error #%d', code);
// Prints: error #5, to stderr
console.error('error', code);
// Prints: error 5, to stderr
If formatting elements (e.g. %d) are not found in the first string then
util.inspect() is called on each argument and the
resulting string values are concatenated. See util.format()
for more information.
จะเห็นว่า function register() เอา function อื่นๆมาใช้งาน จะถูกบังคับให้ต้องดู type ก่อนว่าเป็น Ok หรือ Err ซึ่งเราก็ต้องจัดการกับ Error ก่อน สุดท้ายถ้าทุกอย่างผ่านไปได้ด้วยดีทั้งหมด เราก็ค่อย return ผลลัพธ์จริงๆที่ function register() ต้องส่งกลับไป
Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
@param ― value A JavaScript value, usually an object or array, to be converted.
@param ― replacer A function that transforms the results.
@param ― space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
stringify(
const newUserResult: Ok<User>
newUserResult.
value: User
value),{
ResponseInit.status?: number
status:200})
30
}
31
catch (
function(localvar)error: unknown
error) {
32
var console: Console
The console module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
A global console instance configured to write to process.stdout and
process.stderr. The global console can be used without importing the node:console module.
Warning: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O for
more information.
Example using the global console:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s','world');
// Prints: hello world, to stdout
console.error(newError('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name ='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
const out =getStreamSomehow();
const err =getStreamSomehow();
const myConsole =new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s','world');
// Prints: hello world, to out
myConsole.error(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
Prints to stderr with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()).
const code =5;
console.error('error #%d', code);
// Prints: error #5, to stderr
console.error('error', code);
// Prints: error 5, to stderr
If formatting elements (e.g. %d) are not found in the first string then
util.inspect() is called on each argument and the
resulting string values are concatenated. See util.format()
for more information.
@since ― v0.1.100
error(
function(localvar)error: unknown
error)
33
returnnew
var Response:new(body?: BodyInit,init?: ResponseInit)=> Response
Response(
var JSON: JSON
An intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.
Creates an Effect that represents a recoverable error.
When to Use
Use this function to explicitly signal an error in an Effect. The error
will keep propagating unless it is handled. You can handle the error with
functions like
catchAll
or
catchTag
.
Example (Creating a Failed Effect)
import{ Effect }from"effect"
// ┌─── Effect<never, Error, never>
// ▼
const failure = Effect.fail(
newError("Operation failed due to network error")
)
@see ― succeed to create an effect that represents a successful value.
@since ― 2.0.0
fail("Ok")
const notOk: Effect.Effect<never,string,never>
4
จะเห็นว่าตัวแปร notOk มี type เป็นแบบนี้ Effect.Effect<never, string, never> หมายความว่า
1
┌─── `never` -> ไม่มีทาง run ผ่านเพราะตรงนี้เป็น `never`
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
@since ― 2.0.0
@since ― 2.0.0
Effect<number,string>
ตัวอย่างเช่น
1
import{
import Effect
@since ― 2.0.0
@since ― 2.0.0
@since ― 2.0.0
Effect}from"effect"
2
3
functionisNumberInFormOfString(
numberStr: string
numberStr:string):
import Effect
@since ― 2.0.0
@since ― 2.0.0
@since ― 2.0.0
Effect.
interfaceEffect<out A,out E =never,out R =never>
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers.
Number.
NumberConstructor.parseInt(string: string, radix?: number): number
Converts A string to an integer.
@param ― string A string to convert into a number.
@param ― radix A value between 2 and 36 that specifies the base of the number in string.
If this argument is not supplied, strings with a prefix of '0x' are considered hexadecimal.
All other strings are considered decimal.
parseInt(
numberStr: string
numberStr)
5
if (
var Number: NumberConstructor
An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers.
Number.
NumberConstructor.isNaN(number: unknown): boolean
Returns a Boolean value that indicates whether a value is the reserved value NaN (not a
number). Unlike the global isNaN(), Number.isNaN() doesn't forcefully convert the parameter
to a number. Only values of the type number, that are also NaN, result in true.
Creates an Effect that represents a recoverable error.
When to Use
Use this function to explicitly signal an error in an Effect. The error
will keep propagating unless it is handled. You can handle the error with
functions like
catchAll
or
catchTag
.
Example (Creating a Failed Effect)
import{ Effect }from"effect"
// ┌─── Effect<never, Error, never>
// ▼
const failure = Effect.fail(
newError("Operation failed due to network error")
)
@see ― succeed to create an effect that represents a successful value.
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
@since ― 2.0.0
@since ― 2.0.0
Effect<number,string,never>{
4
const
const num:number
num=
var Number: NumberConstructor
An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers.
Number.
NumberConstructor.parseInt(string: string, radix?: number): number
Converts A string to an integer.
@param ― string A string to convert into a number.
@param ― radix A value between 2 and 36 that specifies the base of the number in string.
If this argument is not supplied, strings with a prefix of '0x' are considered hexadecimal.
All other strings are considered decimal.
parseInt(
numberStr: string
numberStr)
5
if (
var Number: NumberConstructor
An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers.
Number.
NumberConstructor.isNaN(number: unknown): boolean
Returns a Boolean value that indicates whether a value is the reserved value NaN (not a
number). Unlike the global isNaN(), Number.isNaN() doesn't forcefully convert the parameter
to a number. Only values of the type number, that are also NaN, result in true.
Creates an Effect that represents a recoverable error.
When to Use
Use this function to explicitly signal an error in an Effect. The error
will keep propagating unless it is handled. You can handle the error with
functions like
catchAll
or
catchTag
.
Example (Creating a Failed Effect)
import{ Effect }from"effect"
// ┌─── Effect<never, Error, never>
// ▼
const failure = Effect.fail(
newError("Operation failed due to network error")
)
@see ― succeed to create an effect that represents a successful value.
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
@since ― 2.0.0
@since ― 2.0.0
Effect<number,string,never>{
4
const
const num:number
num=
var Number: NumberConstructor
An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers.
Number.
NumberConstructor.parseInt(string: string, radix?: number): number
Converts A string to an integer.
@param ― string A string to convert into a number.
@param ― radix A value between 2 and 36 that specifies the base of the number in string.
If this argument is not supplied, strings with a prefix of '0x' are considered hexadecimal.
All other strings are considered decimal.
parseInt(
numberStr: string
numberStr)
5
if (
var Number: NumberConstructor
An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers.
Number.
NumberConstructor.isNaN(number: unknown): boolean
Returns a Boolean value that indicates whether a value is the reserved value NaN (not a
number). Unlike the global isNaN(), Number.isNaN() doesn't forcefully convert the parameter
to a number. Only values of the type number, that are also NaN, result in true.
Creates an Effect that represents a recoverable error.
When to Use
Use this function to explicitly signal an error in an Effect. The error
will keep propagating unless it is handled. You can handle the error with
functions like
catchAll
or
catchTag
.
Example (Creating a Failed Effect)
import{ Effect }from"effect"
// ┌─── Effect<never, Error, never>
// ▼
const failure = Effect.fail(
newError("Operation failed due to network error")
)
@see ― succeed to create an effect that represents a successful value.
Executes an effect synchronously, running it immediately and returning the
result.
Details
This function evaluates the provided effect synchronously, returning its
result directly. It is ideal for effects that do not fail or include
asynchronous operations. If the effect does fail or involves async tasks, it
will throw an error. Execution stops at the point of failure or asynchronous
operation, making it unsuitable for effects that require asynchronous
handling.
Important: Attempting to run effects that involve asynchronous operations
or failures will result in exceptions being thrown, so use this function with
care for purely synchronous and error-free effects.
When to Use
Use this function when:
You are sure that the effect will not fail or involve asynchronous
operations.
You need a direct, synchronous result from the effect.
You are working within a context where asynchronous effects are not
allowed.
Avoid using this function for effects that can fail or require asynchronous
handling. For such cases, consider using
runPromise
or
runSyncExit
.
Example (Synchronous Logging)
import{ Effect }from"effect"
const program = Effect.sync(()=>{
console.log("Hello, World!")
return1
})
const result = Effect.runSync(program)
// Output: Hello, World!
console.log(result)
// Output: 1
Example (Incorrect Usage with Failing or Async Effects)
import{ Effect }from"effect"
try{
// Attempt to run an effect that fails
Effect.runSync(Effect.fail("my error"))
}catch (e) {
console.error(e)
}
// Output:
// (FiberFailure) Error: my error
try{
// Attempt to run an effect that involves async work
// (FiberFailure) AsyncFiberException: Fiber #0 cannot be resolved synchronously. This is caused by using runSync on an effect that performs async work
@see ― runSyncExit for a version that returns an Exit type instead of
throwing an error.
Executes an effect synchronously, running it immediately and returning the
result.
Details
This function evaluates the provided effect synchronously, returning its
result directly. It is ideal for effects that do not fail or include
asynchronous operations. If the effect does fail or involves async tasks, it
will throw an error. Execution stops at the point of failure or asynchronous
operation, making it unsuitable for effects that require asynchronous
handling.
Important: Attempting to run effects that involve asynchronous operations
or failures will result in exceptions being thrown, so use this function with
care for purely synchronous and error-free effects.
When to Use
Use this function when:
You are sure that the effect will not fail or involve asynchronous
operations.
You need a direct, synchronous result from the effect.
You are working within a context where asynchronous effects are not
allowed.
Avoid using this function for effects that can fail or require asynchronous
handling. For such cases, consider using
runPromise
or
runSyncExit
.
Example (Synchronous Logging)
import{ Effect }from"effect"
const program = Effect.sync(()=>{
console.log("Hello, World!")
return1
})
const result = Effect.runSync(program)
// Output: Hello, World!
console.log(result)
// Output: 1
Example (Incorrect Usage with Failing or Async Effects)
import{ Effect }from"effect"
try{
// Attempt to run an effect that fails
Effect.runSync(Effect.fail("my error"))
}catch (e) {
console.error(e)
}
// Output:
// (FiberFailure) Error: my error
try{
// Attempt to run an effect that involves async work
// (FiberFailure) AsyncFiberException: Fiber #0 cannot be resolved synchronously. This is caused by using runSync on an effect that performs async work
@see ― runSyncExit for a version that returns an Exit type instead of
throwing an error.
ถ้าเรามี function หลายๆ function ที่ return Effect เหมือนกัน แล้วเราอยากเอา function เหล่านั้นมาทำงานร่วมกันเป็น function ที่ใหญ่ขึ้น แก้ปัญหาที่ใหญ่ขึ้น ให้ใช้ pipe() กับ Effect.andThen()
1
import{
import Effect
@since ― 2.0.0
@since ― 2.0.0
@since ― 2.0.0
Effect,
functionpipe<A>(a: A):A(+19 overloads)
Pipes the value of an expression into a pipeline of functions.
Details
The pipe function is a utility that allows us to compose functions in a
readable and sequential manner. It takes the output of one function and
passes it as the input to the next function in the pipeline. This enables us
to build complex transformations by chaining multiple functions together.
import{ pipe }from"effect"
const result =pipe(input, func1, func2,..., funcN)
In this syntax, input is the initial value, and func1, func2, ...,
funcN are the functions to be applied in sequence. The result of each
function becomes the input for the next function, and the final result is
returned.
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
@since ― 2.0.0
@since ― 2.0.0
Effect<number,string>{
4
const
const num:number
num=
var Number: NumberConstructor
An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers.
Number.
NumberConstructor.parseInt(string: string, radix?: number): number
Converts A string to an integer.
@param ― string A string to convert into a number.
@param ― radix A value between 2 and 36 that specifies the base of the number in string.
If this argument is not supplied, strings with a prefix of '0x' are considered hexadecimal.
All other strings are considered decimal.
parseInt(
numberStr: string
numberStr)
5
if (
var Number: NumberConstructor
An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers.
Number.
NumberConstructor.isNaN(number: unknown): boolean
Returns a Boolean value that indicates whether a value is the reserved value NaN (not a
number). Unlike the global isNaN(), Number.isNaN() doesn't forcefully convert the parameter
to a number. Only values of the type number, that are also NaN, result in true.
Creates an Effect that represents a recoverable error.
When to Use
Use this function to explicitly signal an error in an Effect. The error
will keep propagating unless it is handled. You can handle the error with
functions like
catchAll
or
catchTag
.
Example (Creating a Failed Effect)
import{ Effect }from"effect"
// ┌─── Effect<never, Error, never>
// ▼
const failure = Effect.fail(
newError("Operation failed due to network error")
)
@see ― succeed to create an effect that represents a successful value.
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
Creates an Effect that represents a recoverable error.
When to Use
Use this function to explicitly signal an error in an Effect. The error
will keep propagating unless it is handled. You can handle the error with
functions like
catchAll
or
catchTag
.
Example (Creating a Failed Effect)
import{ Effect }from"effect"
// ┌─── Effect<never, Error, never>
// ▼
const failure = Effect.fail(
newError("Operation failed due to network error")
)
@see ― succeed to create an effect that represents a successful value.
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
Creates an Effect that represents an asynchronous computation that might
fail.
When to Use
In situations where you need to perform asynchronous operations that might
fail, such as fetching data from an API, you can use the tryPromise
constructor. This constructor is designed to handle operations that could
throw exceptions by capturing those exceptions and transforming them into
manageable errors.
Error Handling
There are two ways to handle errors with tryPromise:
If you don't provide a catch function, the error is caught and the
effect fails with an UnknownException.
If you provide a catch function, the error is caught and the catch
function maps it to an error of type E.
Interruptions
An optional AbortSignal can be provided to allow for interruption of the
wrapped Promise API.
Example (Fetching a TODO Item)
import{ Effect }from"effect"
constgetTodo=(id:number)=>
// Will catch any errors and propagate them as UnknownException
Pipes the value of an expression into a pipeline of functions.
Details
The pipe function is a utility that allows us to compose functions in a
readable and sequential manner. It takes the output of one function and
passes it as the input to the next function in the pipeline. This enables us
to build complex transformations by chaining multiple functions together.
import{ pipe }from"effect"
const result =pipe(input, func1, func2,..., funcN)
In this syntax, input is the initial value, and func1, func2, ...,
funcN are the functions to be applied in sequence. The result of each
function becomes the input for the next function, and the final result is
returned.
Use andThen when you need to run multiple actions in sequence, with the
second action depending on the result of the first. This is useful for
combining effects or handling computations that must happen in order.
Details
The second action can be:
A constant value (similar to
as
)
A function returning a value (similar to
map
)
A Promise
A function returning a Promise
An Effect
A function returning an Effect (similar to
flatMap
)
Note:andThen works well with both Option and Either types,
treating them as effects.
Example (Applying a Discount Based on Fetched Amount)
import{ pipe, Effect }from"effect"
// Function to apply a discount safely to a transaction amount
constapplyDiscount= (
total:number,
discountRate:number
):Effect.Effect<number, Error>=>
discountRate ===0
? Effect.fail(newError("Discount rate cannot be zero"))
Use andThen when you need to run multiple actions in sequence, with the
second action depending on the result of the first. This is useful for
combining effects or handling computations that must happen in order.
Details
The second action can be:
A constant value (similar to
as
)
A function returning a value (similar to
map
)
A Promise
A function returning a Promise
An Effect
A function returning an Effect (similar to
flatMap
)
Note:andThen works well with both Option and Either types,
treating them as effects.
Example (Applying a Discount Based on Fetched Amount)
import{ pipe, Effect }from"effect"
// Function to apply a discount safely to a transaction amount
constapplyDiscount= (
total:number,
discountRate:number
):Effect.Effect<number, Error>=>
discountRate ===0
? Effect.fail(newError("Discount rate cannot be zero"))
Handles all errors in an effect by providing a fallback effect.
Details
This function catches any errors that may occur during the execution of an
effect and allows you to handle them by specifying a fallback effect. This
ensures that the program continues without failing by recovering from errors
using the provided fallback logic.
Note: This function only handles recoverable errors. It will not recover
from unrecoverable defects.
Example (Providing Recovery Logic for Recoverable Errors)
@see ― catchAllCause for a version that can recover from both
recoverable and unrecoverable errors.
@since ― 2.0.0
catchAll(()=>
import Effect
@since ― 2.0.0
@since ― 2.0.0
@since ― 2.0.0
Effect.
const void: Effect.Effect<void,never,never>
export void
Represents an effect that does nothing and produces no value.
When to Use
Use this effect when you need to represent an effect that does nothing.
This is useful in scenarios where you need to satisfy an effect-based
interface or control program flow without performing any operations. For
example, it can be used in situations where you want to return an effect
from a function but do not need to compute or return any result.
Executes an effect and returns the result as a Promise.
Details
This function runs an effect and converts its result into a Promise. If the
effect succeeds, the Promise will resolve with the successful result. If
the effect fails, the Promise will reject with an error, which includes the
failure details of the effect.
The optional options parameter allows you to pass an AbortSignal for
cancellation, enabling more fine-grained control over asynchronous tasks.
When to Use
Use this function when you need to execute an effect and work with its result
in a promise-based system, such as when integrating with third-party
libraries that expect Promise results.
Example (Running a Successful Effect as a Promise)
Pipes the value of an expression into a pipeline of functions.
Details
The pipe function is a utility that allows us to compose functions in a
readable and sequential manner. It takes the output of one function and
passes it as the input to the next function in the pipeline. This enables us
to build complex transformations by chaining multiple functions together.
import{ pipe }from"effect"
const result =pipe(input, func1, func2,..., funcN)
In this syntax, input is the initial value, and func1, func2, ...,
funcN are the functions to be applied in sequence. The result of each
function becomes the input for the next function, and the final result is
returned.
Represents the completion of an asynchronous operation
Promise<string>
4
5
class
classHashPasswordError
HashPasswordErrorextends
import Data
Data.
constTaggedError:<"HashPasswordError">(tag:"HashPasswordError")=>new<A>(args: Equals<A,{}>extendstrue?void:{readonly [P inkeyof A as P extends"_tag"?never: P]: A[P];})=> YieldableError & ... 1 more ... & Readonly<...>
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
Creates an Effect that represents an asynchronous computation that might
fail.
When to Use
In situations where you need to perform asynchronous operations that might
fail, such as fetching data from an API, you can use the tryPromise
constructor. This constructor is designed to handle operations that could
throw exceptions by capturing those exceptions and transforming them into
manageable errors.
Error Handling
There are two ways to handle errors with tryPromise:
If you don't provide a catch function, the error is caught and the
effect fails with an UnknownException.
If you provide a catch function, the error is caught and the catch
function maps it to an error of type E.
Interruptions
An optional AbortSignal can be provided to allow for interruption of the
wrapped Promise API.
Example (Fetching a TODO Item)
import{ Effect }from"effect"
constgetTodo=(id:number)=>
// Will catch any errors and propagate them as UnknownException
Pipes the value of an expression into a pipeline of functions.
Details
The pipe function is a utility that allows us to compose functions in a
readable and sequential manner. It takes the output of one function and
passes it as the input to the next function in the pipeline. This enables us
to build complex transformations by chaining multiple functions together.
import{ pipe }from"effect"
const result =pipe(input, func1, func2,..., funcN)
In this syntax, input is the initial value, and func1, func2, ...,
funcN are the functions to be applied in sequence. The result of each
function becomes the input for the next function, and the final result is
returned.
Represents the completion of an asynchronous operation
Promise<
type User ={
id:string;
email:string;
hashedPassword:string;
}
User|null>
5
6
class
classFindOneByEmailError
FindOneByEmailErrorextends
import Data
Data.
constTaggedError:<"FindOneByEmailError">(tag:"FindOneByEmailError")=>new<A>(args: Equals<A,{}>extendstrue?void:{readonly [P inkeyof A as P extends"_tag"?never: P]: A[P];})=> YieldableError & ... 1 more ... & Readonly<...>
@since ― 2.0.0
TaggedError("FindOneByEmailError")<{
msg: string
msg:string,
error?: unknown
error?:unknown}>{}
7
8
functionfindOneByEmailWrap(
email: string
email:string):
import Effect
@since ― 2.0.0
@since ― 2.0.0
@since ― 2.0.0
Effect.
interfaceEffect<out A,out E =never,out R =never>
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
@since ― 2.0.0
@since ― 2.0.0
Effect<
import Option
@since ― 2.0.0
@since ― 2.0.0
Option.
type Option<A>= Option.None<A>| Option.Some<A>
The Option data type represents optional values. An Option<A> can either
be Some<A>, containing a value of type A, or None, representing the
absence of a value.
When to Use
You can use Option in scenarios like:
Using it for initial values
Returning values from functions that are not defined for all possible
inputs (referred to as “partial functions”)
Creates an Effect that represents an asynchronous computation that might
fail.
When to Use
In situations where you need to perform asynchronous operations that might
fail, such as fetching data from an API, you can use the tryPromise
constructor. This constructor is designed to handle operations that could
throw exceptions by capturing those exceptions and transforming them into
manageable errors.
Error Handling
There are two ways to handle errors with tryPromise:
If you don't provide a catch function, the error is caught and the
effect fails with an UnknownException.
If you provide a catch function, the error is caught and the catch
function maps it to an error of type E.
Interruptions
An optional AbortSignal can be provided to allow for interruption of the
wrapped Promise API.
Example (Fetching a TODO Item)
import{ Effect }from"effect"
constgetTodo=(id:number)=>
// Will catch any errors and propagate them as UnknownException
map takes a function and applies it to the value contained within an
effect, creating a new effect with the transformed value.
It's important to note that effects are immutable, meaning that the original
effect is not modified. Instead, a new effect is returned with the updated
value.
type Omit<T, K extendskeyofany>={ [P in Exclude<keyof T, K>]: T[P];}
Construct a type with the properties of T except for those in type K.
Omit<
type User ={
id:string;
email:string;
hashedPassword:string;
}
User,"id">):
interfacePromise<T>
Represents the completion of an asynchronous operation
Promise<
type User ={
id:string;
email:string;
hashedPassword:string;
}
User>
19
20
class
classSaveUserError
SaveUserErrorextends
import Data
Data.
constTaggedError:<"SaveUserError">(tag:"SaveUserError")=>new<A>(args: Equals<A,{}>extendstrue?void:{readonly [P inkeyof A as P extends"_tag"?never: P]: A[P];})=> YieldableError &{
...;
}& Readonly<...>
@since ― 2.0.0
TaggedError("SaveUserError")<{
error?: unknown
error?:unknown,
msg: string
msg:string}>{}
21
22
functionsaveUserWrap(
user: Omit<User,"id">
user:
type Omit<T, K extendskeyofany>={ [P in Exclude<keyof T, K>]: T[P];}
Construct a type with the properties of T except for those in type K.
Omit<
type User ={
id:string;
email:string;
hashedPassword:string;
}
User,"id">):
import Effect
@since ― 2.0.0
@since ― 2.0.0
@since ― 2.0.0
Effect.
interfaceEffect<out A,out E =never,out R =never>
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
Creates an Effect that represents an asynchronous computation that might
fail.
When to Use
In situations where you need to perform asynchronous operations that might
fail, such as fetching data from an API, you can use the tryPromise
constructor. This constructor is designed to handle operations that could
throw exceptions by capturing those exceptions and transforming them into
manageable errors.
Error Handling
There are two ways to handle errors with tryPromise:
If you don't provide a catch function, the error is caught and the
effect fails with an UnknownException.
If you provide a catch function, the error is caught and the catch
function maps it to an error of type E.
Interruptions
An optional AbortSignal can be provided to allow for interruption of the
wrapped Promise API.
Example (Fetching a TODO Item)
import{ Effect }from"effect"
constgetTodo=(id:number)=>
// Will catch any errors and propagate them as UnknownException
Pipes the value of an expression into a pipeline of functions.
Details
The pipe function is a utility that allows us to compose functions in a
readable and sequential manner. It takes the output of one function and
passes it as the input to the next function in the pipeline. This enables us
to build complex transformations by chaining multiple functions together.
import{ pipe }from"effect"
const result =pipe(input, func1, func2,..., funcN)
In this syntax, input is the initial value, and func1, func2, ...,
funcN are the functions to be applied in sequence. The result of each
function becomes the input for the next function, and the final result is
returned.
It's important to note that functions passed to pipe must have a single
argument because they are only called with a single argument.
When to Use
This is useful in combination with data-last functions as a simulation of
methods:
as.map(f).filter(g)
becomes:
import{ pipe, Array }from"effect"
pipe(as, Array.map(f), Array.filter(g))
Example (Chaining Arithmetic Operations)
import{ pipe }from"effect"
// Define simple arithmetic operations
constincrement=(x:number)=> x +1
constdouble=(x:number)=> x *2
constsubtractTen=(x:number)=> x -10
// Sequentially apply these operations using `pipe`
const result =pipe(5, increment, double, subtractTen)
console.log(result)
// Output: 2
@since ― 2.0.0
pipe}from"effect"
2
3
function
functionvalidateEmail(email:string):boolean
validateEmail(
email: string
email:string):boolean{
4
const
const emailRegex: RegExp
emailRegex=/^[^\s@]+@[^\s@][^\s.@]*\.[^\s@]+$/
5
return
const emailRegex: RegExp
emailRegex.
RegExp.test(string: string): boolean
Returns a Boolean value that indicates whether or not a pattern exists in a searched string.
@param ― string String on which to perform the search.
test(
email: string
email)
6
}
7
8
class
classEmailInvalid
EmailInvalidextends
import Data
Data.
constTaggedError:<"EmailInvalid">(tag:"EmailInvalid")=>new<A>(args: Equals<A,{}>extendstrue?void:{readonly [P inkeyof A as P extends"_tag"?never: P]: A[P];})=> YieldableError &{
This function defines a Matcher that operates on a given type, allowing you
to specify conditions for handling different cases. Once the matcher is
created, you can use pattern-matching functions like
when
to define
how different values should be processed.
Example (Matching Numbers and Strings)
import{ Match }from"effect"
// Create a matcher for values that are either strings or numbers
//
// ┌─── (u: string | number) => string
// ▼
const match = Match.type<string|number>().pipe(
// Match when the value is a number
Match.when(Match.number,(n)=>`number: ${n}`),
// Match when the value is a string
Match.when(Match.string,(s)=>`string: ${s}`),
// Ensure all possible cases are handled
Match.exhaustive
)
console.log(match(0))
// Output: "number: 0"
console.log(match("hello"))
// Output: "string: hello"
@see ― value for creating a matcher from a specific value.
constwhen:<boolean,false,any,()=> Effect.Effect<never, EmailInvalid,never>>(pattern:false,f:()=> Effect.Effect<never, EmailInvalid,never>)=><I, F, A, Pr>(self: Match.Matcher<...>)=> Match.Matcher<...>
Defines a condition for matching values.
Details
This function enables pattern matching by checking whether a given value
satisfies a condition. It supports both direct value comparisons and
predicate functions. If the condition is met, the associated function is
executed.
This function is useful when defining matchers that need to check for
specific values or apply logical conditions to determine a match. It works
well with structured objects and primitive types.
Example (Matching with Values and Predicates)
import{ Match }from"effect"
// Create a matcher for objects with an "age" property
const match = Match.type<{age:number}>().pipe(
// Match when age is greater than 18
Match.when({age:(age)=> age >18},(user)=>`Age: ${user.age}`),
// Match when age is exactly 18
Match.when({ age:18},()=>"You can vote"),
// Fallback case for all other ages
Match.orElse((user)=>`${user.age} is too young`)
)
console.log(match({ age:20}))
// Output: "Age: 20"
console.log(match({ age:18}))
// Output: "You can vote"
console.log(match({ age:4}))
// Output: "4 is too young"
@see ― whenOr Use this when multiple patterns should match in a single
condition.
@see ― whenAnd Use this when a value must match all provided patterns.
@see ― orElse Provides a fallback when no patterns match.
Creates an Effect that represents a recoverable error.
When to Use
Use this function to explicitly signal an error in an Effect. The error
will keep propagating unless it is handled. You can handle the error with
functions like
catchAll
or
catchTag
.
Example (Creating a Failed Effect)
import{ Effect }from"effect"
// ┌─── Effect<never, Error, never>
// ▼
const failure = Effect.fail(
newError("Operation failed due to network error")
)
@see ― succeed to create an effect that represents a successful value.
@since ― 2.0.0
fail(new
constructor EmailInvalid<{
msg:string;
error?:unknown;
}>(args:{
readonlymsg:string;
readonlyerror?:unknown;
}):EmailInvalid
EmailInvalid({
msg: string
msg:"email is invalid"}))),
14
import Match
Match.
constwhen:<true,true,any,()=> Effect.Effect<boolean,never,never>>(pattern:true,f:()=> Effect.Effect<boolean,never,never>)=><I, F, A, Pr>(self: Match.Matcher<I, F,true, A, Pr,any>)=> Match.Matcher<...>
Defines a condition for matching values.
Details
This function enables pattern matching by checking whether a given value
satisfies a condition. It supports both direct value comparisons and
predicate functions. If the condition is met, the associated function is
executed.
This function is useful when defining matchers that need to check for
specific values or apply logical conditions to determine a match. It works
well with structured objects and primitive types.
Example (Matching with Values and Predicates)
import{ Match }from"effect"
// Create a matcher for objects with an "age" property
const match = Match.type<{age:number}>().pipe(
// Match when age is greater than 18
Match.when({age:(age)=> age >18},(user)=>`Age: ${user.age}`),
// Match when age is exactly 18
Match.when({ age:18},()=>"You can vote"),
// Fallback case for all other ages
Match.orElse((user)=>`${user.age} is too young`)
)
console.log(match({ age:20}))
// Output: "Age: 20"
console.log(match({ age:18}))
// Output: "You can vote"
console.log(match({ age:4}))
// Output: "4 is too young"
@see ― whenOr Use this when multiple patterns should match in a single
condition.
@see ― whenAnd Use this when a value must match all provided patterns.
@see ― orElse Provides a fallback when no patterns match.
Creates an Effect that always succeeds with a given value.
When to Use
Use this function when you need an effect that completes successfully with a
specific value without any errors or external dependencies.
Example (Creating a Successful Effect)
import{ Effect }from"effect"
// Creating an effect that represents a successful scenario
//
// ┌─── Effect<number, never, never>
// ▼
const success = Effect.succeed(42)
@see ― fail to create an effect that represents a failure.
@since ― 2.0.0
succeed(true)),
15
import Match
Match.
constexhaustive:<I, F, A, Pr, Ret>(self: Match.Matcher<I, F,never, A, Pr, Ret>)=> [Pr] extends [never] ?(u: I)=> Unify<A>: Unify<A>
The Match.exhaustive method finalizes the pattern matching process by
ensuring that all possible cases are accounted for. If any case is missing,
TypeScript will produce a type error. This is particularly useful when
working with unions, as it helps prevent unintended gaps in pattern matching.
Example (Ensuring All Cases Are Covered)
import{ Match }from"effect"
// Create a matcher for string or number values
const match = Match.type<string|number>().pipe(
// Match when the value is a number
Match.when(Match.number,(n)=>`number: ${n}`),
// Mark the match as exhaustive, ensuring all cases are handled
// TypeScript will throw an error if any case is missing
//
@since ― 1.0.0
exhaustive,
16
),
17
)
18
19
class
classPasswordInvalid
PasswordInvalidextends
import Data
Data.
constTaggedError:<"PasswordInvalid">(tag:"PasswordInvalid")=>new<A>(args: Equals<A,{}>extendstrue?void:{readonly [P inkeyof A as P extends"_tag"?never: P]: A[P];})=> YieldableError & ... 1 more ... & Readonly<...>
This function defines a Matcher that operates on a given type, allowing you
to specify conditions for handling different cases. Once the matcher is
created, you can use pattern-matching functions like
when
to define
how different values should be processed.
Example (Matching Numbers and Strings)
import{ Match }from"effect"
// Create a matcher for values that are either strings or numbers
//
// ┌─── (u: string | number) => string
// ▼
const match = Match.type<string|number>().pipe(
// Match when the value is a number
Match.when(Match.number,(n)=>`number: ${n}`),
// Match when the value is a string
Match.when(Match.string,(s)=>`string: ${s}`),
// Ensure all possible cases are handled
Match.exhaustive
)
console.log(match(0))
// Output: "number: 0"
console.log(match("hello"))
// Output: "string: hello"
@see ― value for creating a matcher from a specific value.
constwhen:<boolean,false,any,()=> Effect.Effect<never, PasswordInvalid,never>>(pattern:false,f:()=> Effect.Effect<never, PasswordInvalid,never>)=><I, F, A, Pr>(self: Match.Matcher<...>)=> Match.Matcher<...>
Defines a condition for matching values.
Details
This function enables pattern matching by checking whether a given value
satisfies a condition. It supports both direct value comparisons and
predicate functions. If the condition is met, the associated function is
executed.
This function is useful when defining matchers that need to check for
specific values or apply logical conditions to determine a match. It works
well with structured objects and primitive types.
Example (Matching with Values and Predicates)
import{ Match }from"effect"
// Create a matcher for objects with an "age" property
const match = Match.type<{age:number}>().pipe(
// Match when age is greater than 18
Match.when({age:(age)=> age >18},(user)=>`Age: ${user.age}`),
// Match when age is exactly 18
Match.when({ age:18},()=>"You can vote"),
// Fallback case for all other ages
Match.orElse((user)=>`${user.age} is too young`)
)
console.log(match({ age:20}))
// Output: "Age: 20"
console.log(match({ age:18}))
// Output: "You can vote"
console.log(match({ age:4}))
// Output: "4 is too young"
@see ― whenOr Use this when multiple patterns should match in a single
condition.
@see ― whenAnd Use this when a value must match all provided patterns.
@see ― orElse Provides a fallback when no patterns match.
Creates an Effect that represents a recoverable error.
When to Use
Use this function to explicitly signal an error in an Effect. The error
will keep propagating unless it is handled. You can handle the error with
functions like
catchAll
or
catchTag
.
Example (Creating a Failed Effect)
import{ Effect }from"effect"
// ┌─── Effect<never, Error, never>
// ▼
const failure = Effect.fail(
newError("Operation failed due to network error")
)
@see ― succeed to create an effect that represents a successful value.
@since ― 2.0.0
fail(new
constructor PasswordInvalid<{
msg:string;
error?:unknown;
}>(args:{
readonlymsg:string;
readonlyerror?:unknown;
}):PasswordInvalid
PasswordInvalid({
msg: string
msg:"password need to be 8 characters long"}))),
28
import Match
Match.
constwhen:<true,true,any,()=> Effect.Effect<boolean,never,never>>(pattern:true,f:()=> Effect.Effect<boolean,never,never>)=><I, F, A, Pr>(self: Match.Matcher<I, F,true, A, Pr,any>)=> Match.Matcher<...>
Defines a condition for matching values.
Details
This function enables pattern matching by checking whether a given value
satisfies a condition. It supports both direct value comparisons and
predicate functions. If the condition is met, the associated function is
executed.
This function is useful when defining matchers that need to check for
specific values or apply logical conditions to determine a match. It works
well with structured objects and primitive types.
Example (Matching with Values and Predicates)
import{ Match }from"effect"
// Create a matcher for objects with an "age" property
const match = Match.type<{age:number}>().pipe(
// Match when age is greater than 18
Match.when({age:(age)=> age >18},(user)=>`Age: ${user.age}`),
// Match when age is exactly 18
Match.when({ age:18},()=>"You can vote"),
// Fallback case for all other ages
Match.orElse((user)=>`${user.age} is too young`)
)
console.log(match({ age:20}))
// Output: "Age: 20"
console.log(match({ age:18}))
// Output: "You can vote"
console.log(match({ age:4}))
// Output: "4 is too young"
@see ― whenOr Use this when multiple patterns should match in a single
condition.
@see ― whenAnd Use this when a value must match all provided patterns.
@see ― orElse Provides a fallback when no patterns match.
Creates an Effect that always succeeds with a given value.
When to Use
Use this function when you need an effect that completes successfully with a
specific value without any errors or external dependencies.
Example (Creating a Successful Effect)
import{ Effect }from"effect"
// Creating an effect that represents a successful scenario
//
// ┌─── Effect<number, never, never>
// ▼
const success = Effect.succeed(42)
@see ― fail to create an effect that represents a failure.
@since ― 2.0.0
succeed(true)),
29
import Match
Match.
constexhaustive:<I, F, A, Pr, Ret>(self: Match.Matcher<I, F,never, A, Pr, Ret>)=> [Pr] extends [never] ?(u: I)=> Unify<A>: Unify<A>
The Match.exhaustive method finalizes the pattern matching process by
ensuring that all possible cases are accounted for. If any case is missing,
TypeScript will produce a type error. This is particularly useful when
working with unions, as it helps prevent unintended gaps in pattern matching.
Example (Ensuring All Cases Are Covered)
import{ Match }from"effect"
// Create a matcher for string or number values
const match = Match.type<string|number>().pipe(
// Match when the value is a number
Match.when(Match.number,(n)=>`number: ${n}`),
// Mark the match as exhaustive, ensuring all cases are handled
// TypeScript will throw an error if any case is missing
Returns a Boolean value that indicates whether or not a pattern exists in a searched string.
@param ― string String on which to perform the search.
test(
password: string
password)
35
}
36
constisPasswordContainsSpecialCharEffect:(
password: string
password:string)=>
import Effect
@since ― 2.0.0
@since ― 2.0.0
@since ― 2.0.0
Effect.
interfaceEffect<out A,out E =never,out R =never>
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
This function defines a Matcher that operates on a given type, allowing you
to specify conditions for handling different cases. Once the matcher is
created, you can use pattern-matching functions like
when
to define
how different values should be processed.
Example (Matching Numbers and Strings)
import{ Match }from"effect"
// Create a matcher for values that are either strings or numbers
//
// ┌─── (u: string | number) => string
// ▼
const match = Match.type<string|number>().pipe(
// Match when the value is a number
Match.when(Match.number,(n)=>`number: ${n}`),
// Match when the value is a string
Match.when(Match.string,(s)=>`string: ${s}`),
// Ensure all possible cases are handled
Match.exhaustive
)
console.log(match(0))
// Output: "number: 0"
console.log(match("hello"))
// Output: "string: hello"
@see ― value for creating a matcher from a specific value.
constwhen:<boolean,false,any,()=> Effect.Effect<never, PasswordInvalid,never>>(pattern:false,f:()=> Effect.Effect<never, PasswordInvalid,never>)=><I, F, A, Pr>(self: Match.Matcher<...>)=> Match.Matcher<...>
Defines a condition for matching values.
Details
This function enables pattern matching by checking whether a given value
satisfies a condition. It supports both direct value comparisons and
predicate functions. If the condition is met, the associated function is
executed.
This function is useful when defining matchers that need to check for
specific values or apply logical conditions to determine a match. It works
well with structured objects and primitive types.
Example (Matching with Values and Predicates)
import{ Match }from"effect"
// Create a matcher for objects with an "age" property
const match = Match.type<{age:number}>().pipe(
// Match when age is greater than 18
Match.when({age:(age)=> age >18},(user)=>`Age: ${user.age}`),
// Match when age is exactly 18
Match.when({ age:18},()=>"You can vote"),
// Fallback case for all other ages
Match.orElse((user)=>`${user.age} is too young`)
)
console.log(match({ age:20}))
// Output: "Age: 20"
console.log(match({ age:18}))
// Output: "You can vote"
console.log(match({ age:4}))
// Output: "4 is too young"
@see ― whenOr Use this when multiple patterns should match in a single
condition.
@see ― whenAnd Use this when a value must match all provided patterns.
@see ― orElse Provides a fallback when no patterns match.
Creates an Effect that represents a recoverable error.
When to Use
Use this function to explicitly signal an error in an Effect. The error
will keep propagating unless it is handled. You can handle the error with
functions like
catchAll
or
catchTag
.
Example (Creating a Failed Effect)
import{ Effect }from"effect"
// ┌─── Effect<never, Error, never>
// ▼
const failure = Effect.fail(
newError("Operation failed due to network error")
)
@see ― succeed to create an effect that represents a successful value.
@since ― 2.0.0
fail(new
constructor PasswordInvalid<{
msg:string;
error?:unknown;
}>(args:{
readonlymsg:string;
readonlyerror?:unknown;
}):PasswordInvalid
PasswordInvalid({
msg: string
msg:"password need to be contains at least one special character"}))),
38
import Match
Match.
constwhen:<true,true,any,()=> Effect.Effect<boolean,never,never>>(pattern:true,f:()=> Effect.Effect<boolean,never,never>)=><I, F, A, Pr>(self: Match.Matcher<I, F,true, A, Pr,any>)=> Match.Matcher<...>
Defines a condition for matching values.
Details
This function enables pattern matching by checking whether a given value
satisfies a condition. It supports both direct value comparisons and
predicate functions. If the condition is met, the associated function is
executed.
This function is useful when defining matchers that need to check for
specific values or apply logical conditions to determine a match. It works
well with structured objects and primitive types.
Example (Matching with Values and Predicates)
import{ Match }from"effect"
// Create a matcher for objects with an "age" property
const match = Match.type<{age:number}>().pipe(
// Match when age is greater than 18
Match.when({age:(age)=> age >18},(user)=>`Age: ${user.age}`),
// Match when age is exactly 18
Match.when({ age:18},()=>"You can vote"),
// Fallback case for all other ages
Match.orElse((user)=>`${user.age} is too young`)
)
console.log(match({ age:20}))
// Output: "Age: 20"
console.log(match({ age:18}))
// Output: "You can vote"
console.log(match({ age:4}))
// Output: "4 is too young"
@see ― whenOr Use this when multiple patterns should match in a single
condition.
@see ― whenAnd Use this when a value must match all provided patterns.
@see ― orElse Provides a fallback when no patterns match.
Creates an Effect that always succeeds with a given value.
When to Use
Use this function when you need an effect that completes successfully with a
specific value without any errors or external dependencies.
Example (Creating a Successful Effect)
import{ Effect }from"effect"
// Creating an effect that represents a successful scenario
//
// ┌─── Effect<number, never, never>
// ▼
const success = Effect.succeed(42)
@see ― fail to create an effect that represents a failure.
@since ― 2.0.0
succeed(true)),
39
import Match
Match.
constexhaustive:<I, F, A, Pr, Ret>(self: Match.Matcher<I, F,never, A, Pr, Ret>)=> [Pr] extends [never] ?(u: I)=> Unify<A>: Unify<A>
The Match.exhaustive method finalizes the pattern matching process by
ensuring that all possible cases are accounted for. If any case is missing,
TypeScript will produce a type error. This is particularly useful when
working with unions, as it helps prevent unintended gaps in pattern matching.
Example (Ensuring All Cases Are Covered)
import{ Match }from"effect"
// Create a matcher for string or number values
const match = Match.type<string|number>().pipe(
// Match when the value is a number
Match.when(Match.number,(n)=>`number: ${n}`),
// Mark the match as exhaustive, ensuring all cases are handled
// TypeScript will throw an error if any case is missing
//
@since ― 1.0.0
exhaustive,
40
))
จากโค้ดด้านบน ผมสร้าง function ครอบ function ที่ใช้ verfiy email กับ verify password ไว้แล้วให้ return Effect
ผมใช้ flow() หลายคนอาจจะไม่คุ้นเคย ผมเล่าสั้นๆเกี่ยวกับ flow() ว่า มาจาก FP มันเป็นการเอา function เดิมที่มีอยู่แล้ว
มาเขียนเสริมเข้าไปกับอีก function หนึ่งกลายเป็น function ใหม่ที่รับ argument แบบเดิมเด๊ะๆ
พยายามอธิบายไม่รู้ว่าจะงงหนักกว่าเก่าหรือเปล่า เอาไว้ผมจะเขียน blog เล่าเกี่ยวกับ flow() นี้ให้แบบละเอียดอีกทีนะครับ note ว่ามันจะคล้ายๆกับ pipe()
ยังไม่หมด จะเห็นว่าผมใช้ Match ด้วย หลายคนก็อาจจะไม่คุ้นอีกแล้วหรอ
เอาสั้นๆมันก็เหมือนกับ switch-case รุ่น upgrade ที่ powerful มากๆ
ถ้ามีเวลาก็จะเขียน blog เกี่ยวกับตัวนี้ให้เช่นกันครับ
มาทำกันต่อ อีกนิดเดียวครับ
ผมจะทำ function isEmailExist() โดยจะเอาไปใช้ดูว่า email นี้มีอยู่ใน Database แล้วหรือยัง
1
// assume isEmailExist function from our another module that query the User table
Represents the completion of an asynchronous operation
Promise<
type User ={
id:string;
email:string;
hashedPassword:string;
}
User|null>
3
class
classFindOneByEmailError
FindOneByEmailErrorextends
import Data
Data.
constTaggedError:<"FindOneByEmailError">(tag:"FindOneByEmailError")=>new<A>(args: Equals<A,{}>extendstrue?void:{readonly [P inkeyof A as P extends"_tag"?never: P]: A[P];})=> YieldableError & ... 1 more ... & Readonly<...>
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
@since ― 2.0.0
@since ― 2.0.0
Effect<
import Option
@since ― 2.0.0
@since ― 2.0.0
Option.
type Option<A>= Option.None<A>| Option.Some<A>
The Option data type represents optional values. An Option<A> can either
be Some<A>, containing a value of type A, or None, representing the
absence of a value.
When to Use
You can use Option in scenarios like:
Using it for initial values
Returning values from functions that are not defined for all possible
inputs (referred to as “partial functions”)
Creates an Effect that represents an asynchronous computation that might
fail.
When to Use
In situations where you need to perform asynchronous operations that might
fail, such as fetching data from an API, you can use the tryPromise
constructor. This constructor is designed to handle operations that could
throw exceptions by capturing those exceptions and transforming them into
manageable errors.
Error Handling
There are two ways to handle errors with tryPromise:
If you don't provide a catch function, the error is caught and the
effect fails with an UnknownException.
If you provide a catch function, the error is caught and the catch
function maps it to an error of type E.
Interruptions
An optional AbortSignal can be provided to allow for interruption of the
wrapped Promise API.
Example (Fetching a TODO Item)
import{ Effect }from"effect"
constgetTodo=(id:number)=>
// Will catch any errors and propagate them as UnknownException
map takes a function and applies it to the value contained within an
effect, creating a new effect with the transformed value.
It's important to note that effects are immutable, meaning that the original
effect is not modified. Instead, a new effect is returned with the updated
value.
constTaggedError:<"UserAlreadyExist">(tag:"UserAlreadyExist")=>new<A>(args: Equals<A,{}>extendstrue?void:{readonly [P inkeyof A as P extends"_tag"?never: P]: A[P];})=> YieldableError & ... 1 more ... & Readonly<...>
Use andThen when you need to run multiple actions in sequence, with the
second action depending on the result of the first. This is useful for
combining effects or handling computations that must happen in order.
Details
The second action can be:
A constant value (similar to
as
)
A function returning a value (similar to
map
)
A Promise
A function returning a Promise
An Effect
A function returning an Effect (similar to
flatMap
)
Note:andThen works well with both Option and Either types,
treating them as effects.
Example (Applying a Discount Based on Fetched Amount)
import{ pipe, Effect }from"effect"
// Function to apply a discount safely to a transaction amount
constapplyDiscount= (
total:number,
discountRate:number
):Effect.Effect<number, Error>=>
discountRate ===0
? Effect.fail(newError("Discount rate cannot be zero"))
Performs pattern matching on an Option to handle both Some and None
cases.
Details
This function allows you to match against an Option and handle both
scenarios: when the Option is None (i.e., contains no value), and when
the Option is Some (i.e., contains a value). It executes one of the
provided functions based on the case:
If the Option is None, the onNone function is executed and its result
is returned.
If the Option is Some, the onSome function is executed with the
contained value, and its result is returned.
This function provides a concise and functional way to handle optional values
without resorting to if or manual checks, making your code more declarative
and readable.
Represents an effect that does nothing and produces no value.
When to Use
Use this effect when you need to represent an effect that does nothing.
This is useful in scenarios where you need to satisfy an effect-based
interface or control program flow without performing any operations. For
example, it can be used in situations where you want to return an effect
from a function but do not need to compute or return any result.
Creates an Effect that represents a recoverable error.
When to Use
Use this function to explicitly signal an error in an Effect. The error
will keep propagating unless it is handled. You can handle the error with
functions like
catchAll
or
catchTag
.
Example (Creating a Failed Effect)
import{ Effect }from"effect"
// ┌─── Effect<never, Error, never>
// ▼
const failure = Effect.fail(
newError("Operation failed due to network error")
)
@see ― succeed to create an effect that represents a successful value.
The "do simulation" in Effect allows you to write code in a more declarative style, similar to the "do notation" in other programming languages. It provides a way to define variables and perform operations on them using functions like bind and let.
Here's how the do simulation works:
Start the do simulation using the Do value
Within the do simulation scope, you can use the bind function to define variables and bind them to Effect values
You can accumulate multiple bind statements to define multiple variables within the scope
Inside the do simulation scope, you can also use the let function to define variables and bind them to simple values
Runs a side effect with the result of an effect without changing the original
value.
Details
This function works similarly to flatMap, but it ignores the result of the
function passed to it. The value from the previous effect remains available
for the next part of the chain. Note that if the side effect fails, the
entire chain will fail too.
When to Use
Use this function when you want to perform a side effect, like logging or
tracking, without modifying the main value. This is useful when you need to
observe or record an action but want the original value to be passed to the
next step.
Example (Logging a step in a pipeline)
import{ Console, Effect, pipe }from"effect"
// Function to apply a discount safely to a transaction amount
constapplyDiscount= (
total:number,
discountRate:number
):Effect.Effect<number, Error>=>
discountRate ===0
? Effect.fail(newError("Discount rate cannot be zero"))
Runs a side effect with the result of an effect without changing the original
value.
Details
This function works similarly to flatMap, but it ignores the result of the
function passed to it. The value from the previous effect remains available
for the next part of the chain. Note that if the side effect fails, the
entire chain will fail too.
When to Use
Use this function when you want to perform a side effect, like logging or
tracking, without modifying the main value. This is useful when you need to
observe or record an action but want the original value to be passed to the
next step.
Example (Logging a step in a pipeline)
import{ Console, Effect, pipe }from"effect"
// Function to apply a discount safely to a transaction amount
constapplyDiscount= (
total:number,
discountRate:number
):Effect.Effect<number, Error>=>
discountRate ===0
? Effect.fail(newError("Discount rate cannot be zero"))
Runs a side effect with the result of an effect without changing the original
value.
Details
This function works similarly to flatMap, but it ignores the result of the
function passed to it. The value from the previous effect remains available
for the next part of the chain. Note that if the side effect fails, the
entire chain will fail too.
When to Use
Use this function when you want to perform a side effect, like logging or
tracking, without modifying the main value. This is useful when you need to
observe or record an action but want the original value to be passed to the
next step.
Example (Logging a step in a pipeline)
import{ Console, Effect, pipe }from"effect"
// Function to apply a discount safely to a transaction amount
constapplyDiscount= (
total:number,
discountRate:number
):Effect.Effect<number, Error>=>
discountRate ===0
? Effect.fail(newError("Discount rate cannot be zero"))
The "do simulation" in Effect allows you to write code in a more declarative style, similar to the "do notation" in other programming languages. It provides a way to define variables and perform operations on them using functions like bind and let.
Here's how the do simulation works:
Start the do simulation using the Do value
Within the do simulation scope, you can use the bind function to define variables and bind them to Effect values
You can accumulate multiple bind statements to define multiple variables within the scope
Inside the do simulation scope, you can also use the let function to define variables and bind them to simple values
Runs a side effect with the result of an effect without changing the original
value.
Details
This function works similarly to flatMap, but it ignores the result of the
function passed to it. The value from the previous effect remains available
for the next part of the chain. Note that if the side effect fails, the
entire chain will fail too.
When to Use
Use this function when you want to perform a side effect, like logging or
tracking, without modifying the main value. This is useful when you need to
observe or record an action but want the original value to be passed to the
next step.
Example (Logging a step in a pipeline)
import{ Console, Effect, pipe }from"effect"
// Function to apply a discount safely to a transaction amount
constapplyDiscount= (
total:number,
discountRate:number
):Effect.Effect<number, Error>=>
discountRate ===0
? Effect.fail(newError("Discount rate cannot be zero"))
Use andThen when you need to run multiple actions in sequence, with the
second action depending on the result of the first. This is useful for
combining effects or handling computations that must happen in order.
Details
The second action can be:
A constant value (similar to
as
)
A function returning a value (similar to
map
)
A Promise
A function returning a Promise
An Effect
A function returning an Effect (similar to
flatMap
)
Note:andThen works well with both Option and Either types,
treating them as effects.
Example (Applying a Discount Based on Fetched Amount)
import{ pipe, Effect }from"effect"
// Function to apply a discount safely to a transaction amount
constapplyDiscount= (
total:number,
discountRate:number
):Effect.Effect<number, Error>=>
discountRate ===0
? Effect.fail(newError("Discount rate cannot be zero"))
map takes a function and applies it to the value contained within an
effect, creating a new effect with the transformed value.
It's important to note that effects are immutable, meaning that the original
effect is not modified. Instead, a new effect is returned with the updated
value.
Handles multiple errors in a single block of code using their _tag field.
When to Use
catchTags is a convenient way to handle multiple error types at
once. Instead of using
catchTag
multiple times, you can pass an
object where each key is an error type's _tag, and the value is the handler
for that specific error. This allows you to catch and recover from multiple
error types in a single call.
The error type must have a readonly _tag field to use catchTag. This
field is used to identify and match errors.
Example (Handling Multiple Tagged Error Types at Once)
Executes an effect and returns the result as a Promise.
Details
This function runs an effect and converts its result into a Promise. If the
effect succeeds, the Promise will resolve with the successful result. If
the effect fails, the Promise will reject with an error, which includes the
failure details of the effect.
The optional options parameter allows you to pass an AbortSignal for
cancellation, enabling more fine-grained control over asynchronous tasks.
When to Use
Use this function when you need to execute an effect and work with its result
in a promise-based system, such as when integrating with third-party
libraries that expect Promise results.
Example (Running a Successful Effect as a Promise)
controller เป็นคนที่เอา register() มาใช้จะเป็นคนจัดการกับ Error ที่อาจจะเกิดขึ้น ผ่าน Effect.catchTags
Tag ที่เราใส่ใน Data.TaggedError จะมีประโยชน์ตอนนี้แหละ
เมื่อเราจัดการ handle Error แล้ว ไม่ได้ return Effect.fail แล้ว Effect จะทำการตัด fail Type ออกไปให้เราด้วย
ในตัวอย่างด้านบนเมื่อเรา handle error ได้ครบ เอา mouse ไป hover ที่ program จะเห็นว่ามันมี type เป็น Effect.Effect<Response, never>
ซึ่งส่วนที่ Error เป็น never ไปแล้ว
ถ้าเราเอา program ไปรันใน Effect.runPromise มันก็จะไม่มีทาง Error เพราเราได้ handle ไปหมดแล้ว
Conclusion
ถึงตรงนี้หลายๆคนน่าจะเริ่มสนใจ Effect กันแล้ว
ผมจะเขียน blog เกี่ยวกับการใช้งาน Effect มาเรื่อยๆนะครับ
เอาเป็น blog ไปก่อนละกัน ช่วงนี้ก็ยังอัดคลิปไม่ได้ เลี้ยงลูกคนเดียวไม่สะดวกเท่าไรครับ ถ้าอัดแล้วไม่ต้องตัดมันก็จะง่ายสำหรับผมเลย แต่ถ้าไม่ตัดคลิปมันจะยาว
มันก็เลยใช้พลังงานมาก เอ๊ะเหมือนบ่นให้ฟัง 😆😆
Thanks for being here ✨
ขอบคุณทุกคนที่เข้ามาอ่านจนจบนะครับ
blog นี้ค่อนข้างยาว ถ้ามีส่วนใดผิดพลาดสามารถชี้แนะกันได้นะครับ และขออภัยในความผิดพลาดนั้นล่วงหน้านะครับ