Corail
Corail is the ROP(Railway Oriented Programming) Implementation library
What is Railway Oriented Programmging
"Railway-Oriented Programming" (ROP) is a code style that saves developers from exception handling hell
see me for detail: https://fsharpforfunandprofit.com/posts/recipe-part2/
Usage Examples
With functions
rail works like asynchronous-capable pipe
const multi2 = (num) => num * 2;
const sum3 = (num) => num + 3;
const result = await corail.rail(multi2, sum3)(1); // expect 1 * 2 + 3
result; // to be 5
Create failed with throw
You can create an explicitly failed value through the throw
. if fail occured, then the rail
stops running the rest of the functions and returns a failed result
const sum3 = (num) => num + 3;
const multi2 = (num) => num * 2;
const throwData = (data) => {
// Throw something, like an error or a message, so you can see why the rail was failed
throw data;
};
const result = await corail.rail(multi2, throwData, sum3)(1); // 1 * 2 ...x
corail.isFailed(result); // true
result.err; // 2
The result will be 2.
let's see step by step
-
multi2
will works because there are no failed functions -
throwData
will throw the result of multi2 (it is2
) -
sum3
will not work because there are failed function (throwData) - As a result, the result failed, so,
corail.isFailed
evaluates the result as a failture andresult.err
is 2 (thrown value)
With promise
The rail
also works well with Promise
const asyncSum3 = (num) => Promise.resolve(num + 3);
const asyncMulti2 = (num) => Promise.resolve(num * 2);
const result = await corail.rail(asyncMulti2, asyncSum3)(1);
corail.isFailed(result); // false
result; // 5
If more than one promise
is included, the promise
will be returned as a result.
After await
, there will be the expected value
Create failed with Reject
Like throw, if there is rejected promise, then the result failed, stop running the rest of the functions
const asyncSum3 = (num) => Promise.resolve(num + 3);
const asyncMulti2 = (num) => Promise.resolve(num * 2);
const rejectData = (data) => Promise.reject(data);
const result = await corail.rail(asyncMulti2, rejectData, asyncSum3)(1); // 1 * 2 ... x
corail.isFailed(result); // true
result.err; // 2
With async functions
As it worked well with promise, async function also works well
const asyncSum3 = async (num) => await Promise.resolve(num + 3);
const asyncMulti2 = (num) => Promise.resolve(num * 2);
const result = await corail.rail(asyncMulti2, asyncSum3)(1);
corail.isFailed(result); // false
result; // 5
With fetch
const fetchTodo = async (id) => {
const res = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`);
return res.json();
};
const getTitle = (todo) => todo.title;
const result = await corail.rail(fetchTodo, getTitle)(1);
corail.isFailed(result); // false;
result; // "delectus aut autem"
railRight
There is another rail named railRight
. I recommend this to typescript users. Because this function can guess its return value.
// typescript example
const multi2 = (num: number) => num * 2;
const sumStr3 = (num: number) => String(num + 3);
const result = await corail.railRight(sumStr3, multi2)(1); // the result is assumed to be Failed | string
if (corail.isFailed(result)) {
// if result is failed, result is assumed Failed type
return;
}
result; // to be '5' and it is assumed as a string
railRight
runs the rail from right to left (like compose)